/*
* 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;
}
}