2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Collections.Generic;
19 using System.Diagnostics;
22 using System.Runtime.InteropServices;
23 using System.Threading;
24 using System.Threading.Tasks;
26 using NativeUtil = Interop.ImageUtil;
27 using NativeDecoder = Interop.ImageUtil.Decode;
29 namespace Tizen.Multimedia.Util
32 /// This is a base class for image decoders.
34 /// <since_tizen> 4 </since_tizen>
35 public abstract class ImageDecoder : IDisposable
37 private ImageDecoderHandle _handle;
39 private ColorSpace? _colorSpace;
41 internal ImageDecoder(ImageFormat format)
43 NativeDecoder.Create(out _handle).ThrowIfFailed("Failed to create ImageDecoder");
45 Debug.Assert(_handle != null);
51 /// Gets the image format of this decoder.
53 /// <since_tizen> 4 </since_tizen>
54 public ImageFormat InputFormat { get; }
56 private ImageDecoderHandle Handle
60 if (_handle.IsInvalid)
62 throw new ObjectDisposedException(nameof(ImageDecoder));
69 /// Sets the color-space to decode into. The default is <see cref="ColorSpace.Rgba8888"/>.
71 /// <param name="colorSpace">The value indicating color-space to decode into.</param>
72 /// <exception cref="ArgumentException"><paramref name="colorSpace"/> is invalid.</exception>
73 /// <exception cref="NotSupportedException"><paramref name="colorSpace"/> is not supported by the decoder.</exception>
74 /// <seealso cref="ImageUtil.GetSupportedColorSpaces(ImageFormat)"/>
75 /// <since_tizen> 4 </since_tizen>
76 public void SetColorSpace(ColorSpace colorSpace)
78 ValidationUtil.ValidateEnum(typeof(ColorSpace), colorSpace, nameof(ColorSpace));
80 if (ImageUtil.GetSupportedColorSpaces(InputFormat).Contains(colorSpace) == false)
82 throw new NotSupportedException($"{colorSpace.ToString()} is not supported for {InputFormat}.");
85 _colorSpace = colorSpace;
89 /// Decodes an image from the specified file.
91 /// <param name="inputFilePath">The input file path from which to decode.</param>
92 /// <returns>A task that represents the asynchronous decoding operation.</returns>
94 /// Only Graphics Interchange Format(GIF) codec returns more than one frame.<br/>
96 /// http://tizen.org/privilege/mediastorage is needed if <paramref name="inputFilePath"/> is relevant to the media storage.<br/>
97 /// http://tizen.org/privilege/externalstorage is needed if <paramref name="inputFilePath"/> is relevant to the external storage.
99 /// <exception cref="ArgumentNullException"><paramref name="inputFilePath"/> is null.</exception>
100 /// <exception cref="ArgumentException">
101 /// <paramref name="inputFilePath"/> is an empty string.<br/>
103 /// <paramref name="inputFilePath"/> is not a image file.
105 /// <exception cref="FileNotFoundException"><paramref name="inputFilePath"/> does not exists.</exception>
106 /// <exception cref="UnauthorizedAccessException">The caller does not have required permission to access the path.</exception>
107 /// <exception cref="FileFormatException">The format of <paramref name="inputFilePath"/> is not <see cref="InputFormat"/>.</exception>
108 /// <exception cref="ObjectDisposedException">The <see cref="ImageDecoder"/> has already been disposed of.</exception>
109 /// <since_tizen> 4 </since_tizen>
110 public async Task<IEnumerable<BitmapFrame>> DecodeAsync(string inputFilePath)
112 if (inputFilePath == null)
114 throw new ArgumentNullException(nameof(inputFilePath));
117 if (inputFilePath.Length == 0)
119 throw new ArgumentException("path is empty.", nameof(inputFilePath));
122 if (CheckHeader(inputFilePath) == false)
124 throw new FileFormatException("The file has an invalid header.");
127 var pathPtr = Marshal.StringToHGlobalAnsi(inputFilePath);
130 NativeDecoder.SetInputPath(Handle, pathPtr).ThrowIfFailed("Failed to set input file path for decoding");
132 return await DecodeAsync();
136 Marshal.FreeHGlobal(pathPtr);
141 /// Decodes an image from the buffer.
143 /// <param name="inputBuffer">The image buffer from which to decode.</param>
144 /// <returns>A task that represents the asynchronous decoding operation.</returns>
145 /// <remarks>Only Graphics Interchange Format(GIF) codec returns more than one frame.</remarks>
146 /// <exception cref="ArgumentNullException"><paramref name="inputBuffer"/> is null.</exception>
147 /// <exception cref="ArgumentException"><paramref name="inputBuffer"/> is an empty array.</exception>
148 /// <exception cref="FileFormatException">The format of <paramref name="inputBuffer"/> is not <see cref="InputFormat"/>.</exception>
149 /// <exception cref="ObjectDisposedException">The <see cref="ImageDecoder"/> has already been disposed of.</exception>
150 /// <since_tizen> 4 </since_tizen>
151 public Task<IEnumerable<BitmapFrame>> DecodeAsync(byte[] inputBuffer)
153 if (inputBuffer == null)
155 throw new ArgumentNullException(nameof(inputBuffer));
158 if (inputBuffer.Length == 0)
160 throw new ArgumentException("buffer is empty.", nameof(inputBuffer));
163 if (CheckHeader(inputBuffer) == false)
165 throw new FileFormatException("buffer has an invalid header.");
168 NativeDecoder.SetInputBuffer(Handle, inputBuffer, (ulong)inputBuffer.Length).
169 ThrowIfFailed("Failed to set input buffer for decoding");
171 return DecodeAsync();
174 private bool CheckHeader(byte[] input)
176 if (input.Length < Header.Length)
181 for (int i = 0; i < Header.Length; ++i)
183 if (input[i] != Header[i])
192 private bool CheckHeader(string inputFile)
194 using (var fs = File.OpenRead(inputFile))
196 byte[] fileHeader = new byte[Header.Length];
198 if (fs.Read(fileHeader, 0, fileHeader.Length) < Header.Length)
202 return CheckHeader(fileHeader);
206 private IEnumerable<BitmapFrame> RunDecoding()
208 IntPtr imageHandle = IntPtr.Zero;
209 IntPtr outBuffer = IntPtr.Zero;
213 NativeDecoder.DecodeRun(Handle, out imageHandle).ThrowIfFailed("Failed to decode");
215 NativeUtil.GetImage(imageHandle, out int width, out int height, out ImageColorSpace colorspace,
216 out outBuffer, out int size).ThrowIfFailed("Failed to get decoded image.");
218 yield return new BitmapFrame(outBuffer, width, height, size);
222 if (outBuffer != IntPtr.Zero)
224 LibcSupport.Free(outBuffer);
227 if (imageHandle != IntPtr.Zero)
229 NativeUtil.Destroy(imageHandle).ThrowIfFailed("Failed to destroy handle");
234 internal Task<IEnumerable<BitmapFrame>> DecodeAsync()
238 return Task.Factory.StartNew(RunDecoding, CancellationToken.None,
239 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
240 TaskScheduler.Default);
243 internal virtual void Initialize(ImageDecoderHandle handle)
245 if (_colorSpace.HasValue)
247 NativeDecoder.SetColorspace(Handle, _colorSpace.Value.ToImageColorSpace()).ThrowIfFailed("Failed to set color space");
251 internal abstract byte[] Header { get; }
253 #region IDisposable Support
254 private bool _disposed = false;
257 /// Releases the unmanaged resources used by the ImageDecoder.
259 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
260 /// <since_tizen> 4 </since_tizen>
261 protected virtual void Dispose(bool disposing)
274 /// Releases all resources used by the ImageDecoder.
276 /// <since_tizen> 4 </since_tizen>
277 public void Dispose()
285 /// Provides the ability to decode Bitmap (BMP) encoded images.
287 /// <since_tizen> 4 </since_tizen>
288 public class BmpDecoder : ImageDecoder
290 private static readonly byte[] _header = { (byte)'B', (byte)'M' };
293 /// Initializes a new instance of the <see cref="BmpDecoder"/> class.
295 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.</remarks>
296 /// <since_tizen> 4 </since_tizen>
297 public BmpDecoder() : base(ImageFormat.Bmp)
301 internal override byte[] Header => _header;
305 /// Provides the ability to decode the Portable Network Graphics (PNG) encoded images.
307 /// <since_tizen> 4 </since_tizen>
308 public class PngDecoder : ImageDecoder
310 private static readonly byte[] _header = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
313 /// Initializes a new instance of the <see cref="PngDecoder"/> class.
315 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
316 /// <since_tizen> 4 </since_tizen>
317 public PngDecoder() : base(ImageFormat.Png)
321 internal override byte[] Header => _header;
325 /// Provides the ability to decode the Joint Photographic Experts Group (JPEG) encoded images.
327 /// <since_tizen> 4 </since_tizen>
328 public class JpegDecoder : ImageDecoder
330 private static readonly byte[] _header = { 0xFF, 0xD8 };
333 /// A read-only field that represents the default value of <see cref="Downscale"/>.
335 /// <since_tizen> 4 </since_tizen>
336 public static readonly JpegDownscale DefaultJpegDownscale = JpegDownscale.None;
338 private JpegDownscale _jpegDownscale = DefaultJpegDownscale;
341 /// Initializes a new instance of the <see cref="JpegDecoder"/> class.
343 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
344 /// <since_tizen> 4 </since_tizen>
345 public JpegDecoder() : base(ImageFormat.Jpeg)
350 /// Gets or sets the downscale at which the jpeg image should be decoded.
352 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
353 /// <since_tizen> 4 </since_tizen>
354 public JpegDownscale Downscale
358 return _jpegDownscale;
362 ValidationUtil.ValidateEnum(typeof(JpegDownscale), value, nameof(Downscale));
364 _jpegDownscale = value;
368 internal override void Initialize(ImageDecoderHandle handle)
370 base.Initialize(handle);
372 NativeDecoder.SetJpegDownscale(handle, Downscale).ThrowIfFailed("Failed to set downscale for decoding");
375 internal override byte[] Header => _header;
379 /// Provides the ability to decode the Graphics Interchange Format (GIF) encoded images.
381 /// <since_tizen> 4 </since_tizen>
382 public class GifDecoder : ImageDecoder
384 private static readonly byte[] _header = { (byte)'G', (byte)'I', (byte)'F' };
387 /// Initializes a new instance of the <see cref="GifDecoder"/> class.
389 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Gif"/>.</remarks>
390 /// <since_tizen> 4 </since_tizen>
391 public GifDecoder() : base(ImageFormat.Gif)
395 internal override byte[] Header => _header;