/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * 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. */ using System; using System.Collections.Generic; 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; namespace Tizen.Multimedia.Util { /// /// This is a base class for image decoders. /// /// 4 public abstract class ImageDecoder : IDisposable { private ImageDecoderHandle _handle; private ColorSpace? _colorSpace; internal ImageDecoder(ImageFormat format) { Create(out _handle).ThrowIfFailed("Failed to create ImageDecoder"); Debug.Assert(_handle != null); InputFormat = format; } /// /// Gets the image format of this decoder. /// /// 4 public ImageFormat InputFormat { get; } private ImageDecoderHandle Handle { get { if (_handle.IsInvalid) { throw new ObjectDisposedException(nameof(ImageDecoder)); } return _handle; } } /// /// Sets the color-space to decode into. The default is . /// /// The value indicating color-space to decode into. /// is invalid. /// is not supported by the decoder. /// /// 4 public void SetColorSpace(ColorSpace colorSpace) { ValidationUtil.ValidateEnum(typeof(ColorSpace), colorSpace, nameof(ColorSpace)); if (ImageUtil.GetSupportedColorSpaces(InputFormat).Contains(colorSpace) == false) { throw new NotSupportedException($"{colorSpace.ToString()} is not supported for {InputFormat}."); } _colorSpace = colorSpace; } /// /// Decodes an image from the specified file. /// /// The input file path from which to decode. /// A task that represents the asynchronous decoding operation. /// /// Only Graphics Interchange Format(GIF) codec returns more than one frame.
///
/// http://tizen.org/privilege/mediastorage is needed if is relevant to the media storage.
/// http://tizen.org/privilege/externalstorage is needed if is relevant to the external storage. ///
/// is null. /// /// is an empty string.
/// -or-
/// is not a image file. ///
/// does not exists. /// The caller does not have required permission to access the path. /// The format of is not . /// The has already been disposed of. /// 4 public async Task> DecodeAsync(string inputFilePath) { if (inputFilePath == null) { throw new ArgumentNullException(nameof(inputFilePath)); } if (inputFilePath.Length == 0) { throw new ArgumentException("path is empty.", nameof(inputFilePath)); } if (CheckHeader(inputFilePath) == false) { throw new FileFormatException("The file has an invalid header."); } var pathPtr = Marshal.StringToHGlobalAnsi(inputFilePath); try { SetInputPath(Handle, pathPtr).ThrowIfFailed("Failed to set input file path for decoding"); return await DecodeAsync(); } finally { Marshal.FreeHGlobal(pathPtr); } } /// /// Decodes an image from the buffer. /// /// The image buffer from which to decode. /// A task that represents the asynchronous decoding operation. /// Only Graphics Interchange Format(GIF) codec returns more than one frame. /// is null. /// is an empty array. /// The format of is not . /// The has already been disposed of. /// 4 public Task> DecodeAsync(byte[] inputBuffer) { if (inputBuffer == null) { throw new ArgumentNullException(nameof(inputBuffer)); } if (inputBuffer.Length == 0) { throw new ArgumentException("buffer is empty.", nameof(inputBuffer)); } if (CheckHeader(inputBuffer) == false) { throw new FileFormatException("buffer has an invalid header."); } SetInputBuffer(Handle, inputBuffer, (ulong)inputBuffer.Length). ThrowIfFailed("Failed to set input buffer for decoding"); return DecodeAsync(); } private bool CheckHeader(byte[] input) { if (input.Length < Header.Length) { return false; } for (int i = 0; i < Header.Length; ++i) { if (input[i] != Header[i]) { return false; } } return true; } private bool CheckHeader(string inputFile) { using (var fs = File.OpenRead(inputFile)) { byte[] fileHeader = new byte[Header.Length]; if (fs.Read(fileHeader, 0, fileHeader.Length) < Header.Length) { return false; } return CheckHeader(fileHeader); } } private IEnumerable RunDecoding() { IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr))); Marshal.WriteIntPtr(outBuffer, IntPtr.Zero); try { SetOutputBuffer(Handle, outBuffer).ThrowIfFailed("Failed to decode given image"); DecodeRun(Handle, out var width, out var height, out var size). ThrowIfFailed("Failed to decode"); yield return new BitmapFrame(Marshal.ReadIntPtr(outBuffer), width, height, (int)size); } finally { if (Marshal.ReadIntPtr(outBuffer) != IntPtr.Zero) { LibcSupport.Free(Marshal.ReadIntPtr(outBuffer)); } Marshal.FreeHGlobal(outBuffer); } } internal Task> DecodeAsync() { Initialize(Handle); return Task.Factory.StartNew(RunDecoding, CancellationToken.None, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning, TaskScheduler.Default); } internal virtual void Initialize(ImageDecoderHandle handle) { if (_colorSpace.HasValue) { SetColorspace(Handle, _colorSpace.Value.ToImageColorSpace()).ThrowIfFailed("Failed to set color space"); } } internal abstract byte[] Header { get; } #region IDisposable Support private bool _disposed = false; /// /// Releases the unmanaged resources used by the ImageDecoder. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. /// 4 protected virtual void Dispose(bool disposing) { if (!_disposed) { if (_handle != null) { _handle.Dispose(); } _disposed = true; } } /// /// Releases all resources used by the ImageDecoder. /// /// 4 public void Dispose() { Dispose(true); } #endregion } /// /// Provides the ability to decode Bitmap (BMP) encoded images. /// /// 4 public class BmpDecoder : ImageDecoder { private static readonly byte[] _header = { (byte)'B', (byte)'M' }; /// /// Initializes a new instance of the class. /// /// will be the . /// 4 public BmpDecoder() : base(ImageFormat.Bmp) { } internal override byte[] Header => _header; } /// /// Provides the ability to decode the Portable Network Graphics (PNG) encoded images. /// /// 4 public class PngDecoder : ImageDecoder { private static readonly byte[] _header = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; /// /// Initializes a new instance of the class. /// /// will be the . /// 4 public PngDecoder() : base(ImageFormat.Png) { } internal override byte[] Header => _header; } /// /// Provides the ability to decode the Joint Photographic Experts Group (JPEG) encoded images. /// /// 4 public class JpegDecoder : ImageDecoder { private static readonly byte[] _header = { 0xFF, 0xD8 }; /// /// A read-only field that represents the default value of . /// /// 4 public static readonly JpegDownscale DefaultJpegDownscale = JpegDownscale.None; private JpegDownscale _jpegDownscale = DefaultJpegDownscale; /// /// Initializes a new instance of the class. /// /// will be the . /// 4 public JpegDecoder() : base(ImageFormat.Jpeg) { } /// /// Gets or sets the downscale at which the jpeg image should be decoded. /// /// is invalid. /// 4 public JpegDownscale Downscale { get { return _jpegDownscale; } set { ValidationUtil.ValidateEnum(typeof(JpegDownscale), value, nameof(Downscale)); _jpegDownscale = value; } } internal override void Initialize(ImageDecoderHandle handle) { base.Initialize(handle); SetJpegDownscale(handle, Downscale).ThrowIfFailed("Failed to set downscale for decoding"); } internal override byte[] Header => _header; } /// /// Provides the ability to decode the Graphics Interchange Format (GIF) encoded images. /// /// 4 public class GifDecoder : ImageDecoder { private static readonly byte[] _header = { (byte)'G', (byte)'I', (byte)'F' }; /// /// Initializes a new instance of the class. /// /// will be the . /// 4 public GifDecoder() : base(ImageFormat.Gif) { } internal override byte[] Header => _header; } }