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