/*
* 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using static Interop.ImageUtil;
using static Interop.ImageUtil.Transform;
namespace Tizen.Multimedia.Util
{
///
/// A base class for image transformations.
///
public abstract class ImageTransform
{
internal ImageTransform()
{
}
internal async Task RunAsync(TransformHandle handle, MediaPacket source)
{
var tcs = new TaskCompletionSource();
TransformCompletedCallback cb = (nativehandle, errorCode, _) =>
{
if (errorCode == ImageUtilError.None)
{
try
{
tcs.TrySetResult(MediaPacket.From(Marshal.PtrToStructure(nativehandle)));
}
catch (Exception e)
{
tcs.TrySetException(e);
}
}
else
{
tcs.TrySetException(errorCode.ToException("Image transformation failed"));
}
};
using (var cbKeeper = ObjectKeeper.Get(cb))
{
Run(handle, source.GetHandle(), cb).ThrowIfFailed("Failed to transform given packet with " + GetType());
return await tcs.Task;
}
}
internal static TransformHandle CreateHandle()
{
Create(out var handle).ThrowIfFailed("Failed to run ImageTransformer");
Debug.Assert(handle != null);
return handle;
}
internal abstract void Configure(TransformHandle handle);
internal virtual Task ApplyAsync(MediaPacket source)
{
using (TransformHandle handle = CreateHandle())
{
Configure(handle);
return RunAsync(handle, source);
}
}
}
///
/// Represents a collection of objects that can be individually accessed by index.
///
public class ImageTransformCollection : IEnumerable, IList
{
private List _list = new List();
///
/// Initializes a new instance of the ImageTransformCollection class.
///
public ImageTransformCollection()
{
}
///
/// Gets or sets the at the specified index.
///
/// The zero-based index of the to get or set.
/// The at the specified index.
///
/// index is less than 0.\n
/// - or -\n
/// index is equal to or greater than Count.
///
public ImageTransform this[int index]
{
get { return _list[index]; }
set { _list[index] = value; }
}
///
/// Gets the number of items contained in the TransformCollection.
///
public int Count => _list.Count;
bool ICollection.IsReadOnly => false;
///
/// Adds a to the end of the collection.
///
/// The to add.
///
/// accepts null as a valid value for reference types and allows duplicate elements.
///
public void Add(ImageTransform item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
_list.Add(item);
}
///
/// Removes all items.
///
public void Clear() => _list.Clear();
///
/// Determines whether the contains the specified item.
///
/// The to locate in the collection.
/// true if the is found in the collection; otherwise, false.
public bool Contains(ImageTransform item) => _list.Contains(item);
///
/// Copies the items of the collection to an array, starting at the specified array index.
///
/// The one-dimensional array that is the destination of the items copied from the collection.
/// The zero-based index in array at which copying begins.
/// is null.
/// is less than 0.
///
/// The number of elements in the source collection is greater than the available space from arrayIndex to the end of the destination array.
///
public void CopyTo(ImageTransform[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
///
/// Determines the index of the specified item in the collection.
///
/// The to locate in the collection.
/// The index of value if found in the ; otherwise, -1.
public int IndexOf(ImageTransform item) => _list.IndexOf(item);
///
/// Inserts a into the collection at the specified index.
///
/// The zero-based index at which should be inserted.
/// The to insert into the collection.
/// is null.
///
/// index is less than 0.\n
/// -or-\n
/// index is greater than .
///
public void Insert(int index, ImageTransform item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
_list.Insert(index, item);
}
///
/// Removes the first occurrence of the specified from the collection.
///
/// The to remove.
/// true if was removed from the collection; otherwise, false.
public bool Remove(ImageTransform item) => _list.Remove(item);
///
/// Removes the at the specified index.
///
/// The zero-based index to remove.
///
/// index is less than 0.\n
/// - or -\n
/// index is equal to or greater than .
///
public void RemoveAt(int index) => _list.RemoveAt(index);
///
/// Returns an enumerator that can iterate through the collection.
///
/// An enumerator that can be used to iterate through the collection.
public IEnumerator GetEnumerator() => _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
}
// TODO need to improve performance
///
/// Represents a that is a composite of the transforms.
///
public class ImageTransformGroup : ImageTransform
{
///
/// Gets or sets the .
///
public ImageTransformCollection Children { get; set; }
///
/// Initializes a new instance of the ImageTransformGroup class.
///
public ImageTransformGroup()
{
Children = new ImageTransformCollection();
}
internal override void Configure(TransformHandle handle)
{
// intended blank
}
internal override async Task ApplyAsync(MediaPacket source)
{
if (Children.Count == 0)
{
return source;
}
var items = Children;
MediaPacket curPacket = await items[0].ApplyAsync(source);
for (int i = 1; i < items.Count; ++i)
{
var oldPacket = curPacket;
try
{
curPacket = await items[i].ApplyAsync(curPacket);
}
finally
{
oldPacket.Dispose();
}
}
return curPacket;
}
}
///
/// Rotates an image.
///
///
public class RotateTransform : ImageTransform
{
private Rotation _rotation;
///
/// Initializes a new instance of the class.
///
/// The value how to rotate an image.
/// is invalid.
/// is .
public RotateTransform(Rotation rotation)
{
Rotation = rotation;
}
///
/// Gets or sets the value how to rotate an image.
///
/// is invalid.
/// is .
public Rotation Rotation
{
get { return _rotation; }
set
{
ValidationUtil.ValidateEnum(typeof(Rotation), value, nameof(Rotation));
if (value == Rotation.Rotate0)
{
throw new ArgumentOutOfRangeException(nameof(value), "Rotation can't be Rotate0.");
}
_rotation = value;
}
}
internal override void Configure(TransformHandle handle)
{
SetRotation(handle, GetImageRotation());
}
private ImageRotation GetImageRotation()
{
switch (Rotation)
{
case Rotation.Rotate90: return ImageRotation.Rotate90;
case Rotation.Rotate180: return ImageRotation.Rotate180;
case Rotation.Rotate270: return ImageRotation.Rotate270;
}
Debug.Fail("Rotation is invalid value!");
return ImageRotation.Rotate0;
}
}
///
/// Flips an image.
///
///
public class FlipTransform : ImageTransform
{
private Flips _flip;
///
/// Initializes a new instance of the class.
///
/// The value how to flip an image.
/// is invalid.
/// is .
public FlipTransform(Flips flip)
{
Flip = flip;
}
///
/// Gets or sets the value how to rotate an image.
///
/// is invalid.
/// is .
public Flips Flip
{
get { return _flip; }
set
{
ValidationUtil.ValidateFlagsEnum(value, Flips.Horizontal | Flips.Vertical, nameof(Flips));
if (value == Flips.None)
{
throw new ArgumentOutOfRangeException(nameof(value), "Flip can't be None.");
}
_flip = value;
}
}
internal override void Configure(TransformHandle handle)
{
// intended blank
}
private async Task ApplyAsync(TransformHandle handle, MediaPacket source,
ImageRotation rotation)
{
SetRotation(handle, rotation);
return await RunAsync(handle, source);
}
internal override async Task ApplyAsync(MediaPacket source)
{
using (TransformHandle handle = CreateHandle())
{
if (Flip.HasFlag(Flips.Vertical | Flips.Horizontal))
{
var flipped = await ApplyAsync(handle, source, ImageRotation.FlipHorizontal);
try
{
return await ApplyAsync(handle, flipped, ImageRotation.FlipVertical);
}
finally
{
flipped.Dispose();
}
}
return await ApplyAsync(handle, source, Flip.HasFlag(Flips.Horizontal) ?
ImageRotation.FlipHorizontal : ImageRotation.FlipVertical);
}
}
}
///
/// Changes the colorspace of an image.
///
///
public class ColorSpaceTransform : ImageTransform
{
private ImageColorSpace _imageColorSpace;
///
/// Initializes a new instance of the class.
///
/// The colorspace of output image.
/// is invalid.
/// is not supported.
///
public ColorSpaceTransform(ColorSpace colorSpace)
{
ColorSpace = colorSpace;
}
///
/// Gets or sets the colorspace of the result image.
///
/// is invalid.
/// is not supported.
///
public ColorSpace ColorSpace
{
get { return _imageColorSpace.ToCommonColorSpace(); }
set
{
ValidationUtil.ValidateEnum(typeof(ColorSpace), value, nameof(ColorSpace));
_imageColorSpace = value.ToImageColorSpace();
}
}
internal override void Configure(TransformHandle handle)
{
SetColorspace(handle, _imageColorSpace);
}
///
/// Gets the supported colorspaces for .
///
public static IEnumerable SupportedColorSpaces
{
get
{
foreach (ImageColorSpace value in Enum.GetValues(typeof(ImageColorSpace)))
{
yield return value.ToCommonColorSpace();
}
}
}
}
///
/// Crops an image.
///
public class CropTransform : ImageTransform
{
private Rectangle _region;
///
/// Initializes a new instance of the class.
///
/// The crop region.
///
/// The X-position of is less than zero.\n
/// - or -\n
/// The Y-position of is less than zero.\n
/// - or -\n
/// The width of is less than or equal to zero.\n
/// - or -\n
/// The height of is less than or equal to zero.
///
public CropTransform(Rectangle region)
{
Region = region;
}
///
/// Gets or sets the crop region.
///
///
/// The X-position of is less than zero.\n
/// - or -\n
/// The Y-position of is less than zero.\n
/// - or -\n
/// The width of is less than or equal to zero.\n
/// - or -\n
/// The height of is less than or equal to zero.
///
public Rectangle Region
{
get { return _region; }
set
{
if (value.X < 0)
{
throw new ArgumentOutOfRangeException(nameof(Region), value,
"X position of the region can't be less than zero.");
}
if (value.Y < 0)
{
throw new ArgumentOutOfRangeException(nameof(Region), value,
"Y position of the region can't be less than zero.");
}
if (value.Width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(Region), value,
"Width of the region can't be less than or equal zero.");
}
if (value.Height < 0)
{
throw new ArgumentOutOfRangeException(nameof(Region), value,
"Height of the region can't be less than or equal to zero.");
}
_region = value;
}
}
internal override void Configure(TransformHandle handle)
{
SetCropArea(handle, Region.Left, Region.Top, Region.Right, Region.Bottom);
}
}
///
/// Resizes an image.
///
public class ResizeTransform : ImageTransform
{
private Size _size;
///
/// Initializes a new instance of the class.
///
/// The size that an image is resized to.
///
/// The width of is less than or equal to zero.\n
/// - or -\n
/// The height of is less than or equal to zero.
///
public ResizeTransform(Size size)
{
Size = size;
}
///
/// Gets or sets the size that an image is resized to.
///
///
/// The width of is less than or equal to zero.\n
/// - or -\n
/// The height of is less than or equal to zero.
///
public Size Size
{
get { return _size; }
set
{
if (value.Width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(Size), value,
"Width of the size can't be less than or equal to zero.");
}
if (value.Height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(Size), value,
"Height of the size can't be less than or equal to zero.");
}
_size = value;
}
}
internal override void Configure(TransformHandle handle)
{
SetResolution(handle, (uint)Size.Width, (uint)Size.Height);
}
}
}