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 static Interop.Decode;
28 namespace Tizen.Multimedia.Util
31 /// This is a base class for image decoders.
33 /// <since_tizen> 4 </since_tizen>
34 public abstract class ImageDecoder : IDisposable
36 private ImageDecoderHandle _handle;
38 private ColorSpace? _colorSpace;
40 internal ImageDecoder(ImageFormat format)
42 Create(out _handle).ThrowIfFailed("Failed to create ImageDecoder");
44 Debug.Assert(_handle != null);
50 /// Gets the image format of this decoder.
52 /// <since_tizen> 4 </since_tizen>
53 public ImageFormat InputFormat { get; }
55 private ImageDecoderHandle Handle
59 if (_handle.IsInvalid)
61 throw new ObjectDisposedException(nameof(ImageDecoder));
68 /// Sets the color-space to decode into. The default is <see cref="ColorSpace.Rgba8888"/>.
70 /// <param name="colorSpace">The value indicating color-space to decode into.</param>
71 /// <exception cref="ArgumentException"><paramref name="colorSpace"/> is invalid.</exception>
72 /// <exception cref="NotSupportedException"><paramref name="colorSpace"/> is not supported by the decoder.</exception>
73 /// <seealso cref="ImageUtil.GetSupportedColorSpaces(ImageFormat)"/>
74 /// <since_tizen> 4 </since_tizen>
75 public void SetColorSpace(ColorSpace colorSpace)
77 ValidationUtil.ValidateEnum(typeof(ColorSpace), colorSpace, nameof(ColorSpace));
79 if (ImageUtil.GetSupportedColorSpaces(InputFormat).Contains(colorSpace) == false)
81 throw new NotSupportedException($"{colorSpace.ToString()} is not supported for {InputFormat}.");
84 _colorSpace = colorSpace;
88 /// Decodes an image from the specified file.
90 /// <param name="inputFilePath">The input file path from which to decode.</param>
91 /// <returns>A task that represents the asynchronous decoding operation.</returns>
93 /// Only Graphics Interchange Format(GIF) codec returns more than one frame.<br/>
95 /// http://tizen.org/privilege/mediastorage is needed if <paramref name="inputFilePath"/> is relevant to the media storage.<br/>
96 /// http://tizen.org/privilege/externalstorage is needed if <paramref name="inputFilePath"/> is relevant to the external storage.
98 /// <exception cref="ArgumentNullException"><paramref name="inputFilePath"/> is null.</exception>
99 /// <exception cref="ArgumentException">
100 /// <paramref name="inputFilePath"/> is an empty string.<br/>
102 /// <paramref name="inputFilePath"/> is not a image file.
104 /// <exception cref="FileNotFoundException"><paramref name="inputFilePath"/> does not exists.</exception>
105 /// <exception cref="UnauthorizedAccessException">The caller does not have required permission to access the path.</exception>
106 /// <exception cref="FileFormatException">The format of <paramref name="inputFilePath"/> is not <see cref="InputFormat"/>.</exception>
107 /// <exception cref="ObjectDisposedException">The <see cref="ImageDecoder"/> has already been disposed of.</exception>
108 /// <since_tizen> 4 </since_tizen>
109 public async Task<IEnumerable<BitmapFrame>> DecodeAsync(string inputFilePath)
111 if (inputFilePath == null)
113 throw new ArgumentNullException(nameof(inputFilePath));
116 if (inputFilePath.Length == 0)
118 throw new ArgumentException("path is empty.", nameof(inputFilePath));
121 if (CheckHeader(inputFilePath) == false)
123 throw new FileFormatException("The file has an invalid header.");
126 var pathPtr = Marshal.StringToHGlobalAnsi(inputFilePath);
129 SetInputPath(Handle, pathPtr).ThrowIfFailed("Failed to set input file path for decoding");
130 return await DecodeAsync();
134 Marshal.FreeHGlobal(pathPtr);
139 /// Decodes an image from the buffer.
141 /// <param name="inputBuffer">The image buffer from which to decode.</param>
142 /// <returns>A task that represents the asynchronous decoding operation.</returns>
143 /// <remarks>Only Graphics Interchange Format(GIF) codec returns more than one frame.</remarks>
144 /// <exception cref="ArgumentNullException"><paramref name="inputBuffer"/> is null.</exception>
145 /// <exception cref="ArgumentException"><paramref name="inputBuffer"/> is an empty array.</exception>
146 /// <exception cref="FileFormatException">The format of <paramref name="inputBuffer"/> is not <see cref="InputFormat"/>.</exception>
147 /// <exception cref="ObjectDisposedException">The <see cref="ImageDecoder"/> has already been disposed of.</exception>
148 /// <since_tizen> 4 </since_tizen>
149 public Task<IEnumerable<BitmapFrame>> DecodeAsync(byte[] inputBuffer)
151 if (inputBuffer == null)
153 throw new ArgumentNullException(nameof(inputBuffer));
156 if (inputBuffer.Length == 0)
158 throw new ArgumentException("buffer is empty.", nameof(inputBuffer));
161 if (CheckHeader(inputBuffer) == false)
163 throw new FileFormatException("buffer has an invalid header.");
166 SetInputBuffer(Handle, inputBuffer, (ulong)inputBuffer.Length).
167 ThrowIfFailed("Failed to set input buffer for decoding");
169 return DecodeAsync();
172 private bool CheckHeader(byte[] input)
174 if (input.Length < Header.Length)
179 for (int i = 0; i < Header.Length; ++i)
181 if (input[i] != Header[i])
190 private bool CheckHeader(string inputFile)
192 using (var fs = File.OpenRead(inputFile))
194 byte[] fileHeader = new byte[Header.Length];
196 if (fs.Read(fileHeader, 0, fileHeader.Length) < Header.Length)
200 return CheckHeader(fileHeader);
204 private IEnumerable<BitmapFrame> RunDecoding()
206 IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
207 Marshal.WriteIntPtr(outBuffer, IntPtr.Zero);
211 SetOutputBuffer(Handle, outBuffer).ThrowIfFailed("Failed to decode given image");
213 DecodeRun(Handle, out var width, out var height, out var size).
214 ThrowIfFailed("Failed to decode");
216 yield return new BitmapFrame(Marshal.ReadIntPtr(outBuffer), width, height, (int)size);
220 if (Marshal.ReadIntPtr(outBuffer) != IntPtr.Zero)
222 LibcSupport.Free(Marshal.ReadIntPtr(outBuffer));
225 Marshal.FreeHGlobal(outBuffer);
229 internal Task<IEnumerable<BitmapFrame>> DecodeAsync()
233 return Task.Factory.StartNew(RunDecoding, CancellationToken.None,
234 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
235 TaskScheduler.Default);
238 internal virtual void Initialize(ImageDecoderHandle handle)
240 if (_colorSpace.HasValue)
242 SetColorspace(Handle, _colorSpace.Value.ToImageColorSpace()).ThrowIfFailed("Failed to set color space");
246 internal abstract byte[] Header { get; }
248 #region IDisposable Support
249 private bool _disposed = false;
252 /// Releases the unmanaged resources used by the ImageDecoder.
254 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
255 /// <since_tizen> 4 </since_tizen>
256 protected virtual void Dispose(bool disposing)
269 /// Releases all resources used by the ImageDecoder.
271 /// <since_tizen> 4 </since_tizen>
272 public void Dispose()
280 /// Provides the ability to decode Bitmap (BMP) encoded images.
282 /// <since_tizen> 4 </since_tizen>
283 public class BmpDecoder : ImageDecoder
285 private static readonly byte[] _header = { (byte)'B', (byte)'M' };
288 /// Initializes a new instance of the <see cref="BmpDecoder"/> class.
290 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.</remarks>
291 /// <since_tizen> 4 </since_tizen>
292 public BmpDecoder() : base(ImageFormat.Bmp)
296 internal override byte[] Header => _header;
300 /// Provides the ability to decode the Portable Network Graphics (PNG) encoded images.
302 /// <since_tizen> 4 </since_tizen>
303 public class PngDecoder : ImageDecoder
305 private static readonly byte[] _header = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
308 /// Initializes a new instance of the <see cref="PngDecoder"/> class.
310 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
311 /// <since_tizen> 4 </since_tizen>
312 public PngDecoder() : base(ImageFormat.Png)
316 internal override byte[] Header => _header;
320 /// Provides the ability to decode the Joint Photographic Experts Group (JPEG) encoded images.
322 /// <since_tizen> 4 </since_tizen>
323 public class JpegDecoder : ImageDecoder
325 private static readonly byte[] _header = { 0xFF, 0xD8 };
328 /// A read-only field that represents the default value of <see cref="Downscale"/>.
330 /// <since_tizen> 4 </since_tizen>
331 public static readonly JpegDownscale DefaultJpegDownscale = JpegDownscale.None;
333 private JpegDownscale _jpegDownscale = DefaultJpegDownscale;
336 /// Initializes a new instance of the <see cref="JpegDecoder"/> class.
338 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
339 /// <since_tizen> 4 </since_tizen>
340 public JpegDecoder() : base(ImageFormat.Jpeg)
345 /// Gets or sets the downscale at which the jpeg image should be decoded.
347 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
348 /// <since_tizen> 4 </since_tizen>
349 public JpegDownscale Downscale
353 return _jpegDownscale;
357 ValidationUtil.ValidateEnum(typeof(JpegDownscale), value, nameof(Downscale));
359 _jpegDownscale = value;
363 internal override void Initialize(ImageDecoderHandle handle)
365 base.Initialize(handle);
367 SetJpegDownscale(handle, Downscale).ThrowIfFailed("Failed to set downscale for decoding");
370 internal override byte[] Header => _header;
374 /// Provides the ability to decode the Graphics Interchange Format (GIF) encoded images.
376 /// <since_tizen> 4 </since_tizen>
377 public class GifDecoder : ImageDecoder
379 private static readonly byte[] _header = { (byte)'G', (byte)'I', (byte)'F' };
382 /// Initializes a new instance of the <see cref="GifDecoder"/> class.
384 /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Gif"/>.</remarks>
385 /// <since_tizen> 4 </since_tizen>
386 public GifDecoder() : base(ImageFormat.Gif)
390 internal override byte[] Header => _header;