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;
19 using System.Collections.Generic;
20 using System.Diagnostics;
21 using System.Runtime.InteropServices;
22 using System.Threading.Tasks;
23 using static Interop.ImageUtil;
24 using static Interop.ImageUtil.Transform;
26 namespace Tizen.Multimedia.Util
29 /// A base class for image transformations.
31 public abstract class ImageTransform
33 internal ImageTransform()
37 internal async Task<MediaPacket> RunAsync(TransformHandle handle, MediaPacket source)
39 var tcs = new TaskCompletionSource<MediaPacket>();
41 TransformCompletedCallback cb = (nativehandle, errorCode, _) =>
43 if (errorCode == ImageUtilError.None)
47 tcs.TrySetResult(MediaPacket.From(Marshal.PtrToStructure<IntPtr>(nativehandle)));
51 tcs.TrySetException(e);
56 tcs.TrySetException(errorCode.ToException("Image transformation failed"));
60 using (var cbKeeper = ObjectKeeper.Get(cb))
62 Run(handle, source.GetHandle(), cb).ThrowIfFailed("Failed to transform given packet with " + GetType());
64 return await tcs.Task;
68 internal static TransformHandle CreateHandle()
70 Create(out var handle).ThrowIfFailed("Failed to run ImageTransformer");
71 Debug.Assert(handle != null);
75 internal abstract void Configure(TransformHandle handle);
77 internal virtual Task<MediaPacket> ApplyAsync(MediaPacket source)
79 using (TransformHandle handle = CreateHandle())
83 return RunAsync(handle, source);
89 /// Represents a collection of <see cref="ImageTransform"/> objects that can be individually accessed by index.
91 public class ImageTransformCollection : IEnumerable<ImageTransform>, IList<ImageTransform>
93 private List<ImageTransform> _list = new List<ImageTransform>();
96 /// Initializes a new instance of the ImageTransformCollection class.
98 public ImageTransformCollection()
103 /// Gets or sets the <see cref="ImageTransform"/> at the specified index.
105 /// <param name="index">The zero-based index of the <see cref="ImageTransform"/> to get or set.</param>
106 /// <value>The <see cref="ImageTransform"/> at the specified index.</value>
107 /// <exception cref="ArgumentOutOfRangeException">
108 /// index is less than 0.<br/>
110 /// index is equal to or greater than <see cref="Count"/>.
112 public ImageTransform this[int index]
114 get { return _list[index]; }
115 set { _list[index] = value; }
119 /// Gets the number of items contained in the TransformCollection.
121 public int Count => _list.Count;
123 bool ICollection<ImageTransform>.IsReadOnly => false;
126 /// Adds a <see cref="ImageTransform"/> to the end of the collection.
128 /// <param name="item">The <see cref="ImageTransform"/> to add.</param>
130 /// <see cref="ImageTransformCollection"/> accepts null as a valid value for reference types and allows duplicate elements.
132 public void Add(ImageTransform item)
136 throw new ArgumentNullException(nameof(item));
142 /// Removes all items.
144 public void Clear() => _list.Clear();
147 /// Determines whether the <see cref="ImageTransformCollection"/> contains the specified item.
149 /// <param name="item">The <see cref="ImageTransform"/> to locate in the collection.</param>
150 /// <returns>true if the <see cref="ImageTransform"/> is found in the collection; otherwise, false.</returns>
151 public bool Contains(ImageTransform item) => _list.Contains(item);
154 /// Copies the items of the collection to an array, starting at the specified array index.
156 /// <param name="array">The one-dimensional array that is the destination of the items copied from the collection.</param>
157 /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
158 /// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception>
159 /// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception>
160 /// <exception cref="ArgumentException">
161 /// The number of elements in the source collection is greater than the available space from arrayIndex to the end of the destination array.
163 public void CopyTo(ImageTransform[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
166 /// Determines the index of the specified item in the collection.
168 /// <param name="item">The <see cref="ImageTransform"/> to locate in the collection.</param>
169 /// <returns>The index of value if found in the <see cref="ImageTransformCollection"/>; otherwise, -1.</returns>
170 public int IndexOf(ImageTransform item) => _list.IndexOf(item);
173 /// Inserts a <see cref="ImageTransform"/> into the collection at the specified index.
175 /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
176 /// <param name="item">The <see cref="ImageTransform"/> to insert into the collection.</param>
177 /// <exception cref="ArgumentNullException"><paramref name="item"/> is null.</exception>
178 /// <exception cref="ArgumentOutOfRangeException">
179 /// index is less than 0.<br/>
181 /// index is greater than <see cref="Count"/>.
183 public void Insert(int index, ImageTransform item)
187 throw new ArgumentNullException(nameof(item));
189 _list.Insert(index, item);
193 /// Removes the first occurrence of the specified <see cref="ImageTransform"/> from the collection.
195 /// <param name="item">The <see cref="ImageTransform"/> to remove.</param>
196 /// <returns>true if <paramref name="item"/> was removed from the collection; otherwise, false.</returns>
197 public bool Remove(ImageTransform item) => _list.Remove(item);
200 /// Removes the <see cref="ImageTransform"/> at the specified index.
202 /// <param name="index">The zero-based index to remove.</param>
203 /// <exception cref="ArgumentOutOfRangeException">
204 /// index is less than 0.<br/>
206 /// index is equal to or greater than <see cref="Count"/>.
208 public void RemoveAt(int index) => _list.RemoveAt(index);
211 /// Returns an enumerator that can iterate through the collection.
213 /// <returns>An enumerator that can be used to iterate through the collection.</returns>
214 public IEnumerator<ImageTransform> GetEnumerator() => _list.GetEnumerator();
216 IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
219 // TODO need to improve performance
221 /// Represents a <see cref="ImageTransform"/> that is a composite of the transforms.
223 public class ImageTransformGroup : ImageTransform
226 /// Gets or sets the <see cref="ImageTransformCollection"/>.
228 public ImageTransformCollection Children { get; set; }
231 /// Initializes a new instance of the ImageTransformGroup class.
233 public ImageTransformGroup()
235 Children = new ImageTransformCollection();
238 internal override void Configure(TransformHandle handle)
243 internal override async Task<MediaPacket> ApplyAsync(MediaPacket source)
245 if (Children.Count == 0)
250 var items = Children;
252 MediaPacket curPacket = await items[0].ApplyAsync(source);
254 for (int i = 1; i < items.Count; ++i)
256 var oldPacket = curPacket;
259 curPacket = await items[i].ApplyAsync(curPacket);
272 /// Rotates an image.
274 /// <seealso cref="Rotation"/>
275 public class RotateTransform : ImageTransform
277 private Rotation _rotation;
280 /// Initializes a new instance of the <see cref="RotateTransform"/> class.
282 /// <param name="rotation">The value how to rotate an image.</param>
283 /// <exception cref="ArgumentException"><paramref name="rotation"/> is invalid.</exception>
284 /// <exception cref="ArgumentOutOfRangeException"><paramref name="rotation"/> is <see cref="Rotation.Rotate90"/>.</exception>
285 public RotateTransform(Rotation rotation)
292 /// Gets or sets the value how to rotate an image.
294 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
295 /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> is <see cref="Rotation.Rotate90"/>.</exception>
296 public Rotation Rotation
298 get { return _rotation; }
301 ValidationUtil.ValidateEnum(typeof(Rotation), value, nameof(Rotation));
303 if (value == Rotation.Rotate0)
305 throw new ArgumentOutOfRangeException(nameof(value), "Rotation can't be Rotate0.");
312 internal override void Configure(TransformHandle handle)
314 SetRotation(handle, GetImageRotation());
317 private ImageRotation GetImageRotation()
321 case Rotation.Rotate90: return ImageRotation.Rotate90;
322 case Rotation.Rotate180: return ImageRotation.Rotate180;
323 case Rotation.Rotate270: return ImageRotation.Rotate270;
326 Debug.Fail("Rotation is invalid value!");
327 return ImageRotation.Rotate0;
335 /// <seealso cref="Rotation"/>
336 public class FlipTransform : ImageTransform
341 /// Initializes a new instance of the <see cref="FlipTransform"/> class.
343 /// <param name="flip">The value how to flip an image.</param>
344 /// <exception cref="ArgumentException"><paramref name="flip"/> is invalid.</exception>
345 /// <exception cref="ArgumentOutOfRangeException"><paramref name="flip"/> is <see cref="Flips.None"/>.</exception>
346 public FlipTransform(Flips flip)
352 /// Gets or sets the value how to flip an image.
354 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
355 /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> is <see cref="Flips.None"/>.</exception>
358 get { return _flip; }
361 ValidationUtil.ValidateFlagsEnum(value, Flips.Horizontal | Flips.Vertical, nameof(Flips));
363 if (value == Flips.None)
365 throw new ArgumentOutOfRangeException(nameof(value), "Flip can't be None.");
372 internal override void Configure(TransformHandle handle)
377 private async Task<MediaPacket> ApplyAsync(TransformHandle handle, MediaPacket source,
378 ImageRotation rotation)
380 SetRotation(handle, rotation);
381 return await RunAsync(handle, source);
384 internal override async Task<MediaPacket> ApplyAsync(MediaPacket source)
386 using (TransformHandle handle = CreateHandle())
388 if (Flip.HasFlag(Flips.Vertical | Flips.Horizontal))
390 var flipped = await ApplyAsync(handle, source, ImageRotation.FlipHorizontal);
393 return await ApplyAsync(handle, flipped, ImageRotation.FlipVertical);
401 return await ApplyAsync(handle, source, Flip.HasFlag(Flips.Horizontal) ?
402 ImageRotation.FlipHorizontal : ImageRotation.FlipVertical);
408 /// Changes the colorspace of an image.
410 /// <seealso cref="ColorSpace"/>
411 public class ColorSpaceTransform : ImageTransform
413 private ImageColorSpace _imageColorSpace;
416 /// Initializes a new instance of the <see cref="ColorSpaceTransform"/> class.
418 /// <param name="colorSpace">The colorspace of output image.</param>
419 /// <exception cref="ArgumentException"><paramref name="colorSpace"/> is invalid.</exception>
420 /// <exception cref="NotSupportedException"><paramref name="colorSpace"/> is not supported.</exception>
421 /// <seealso cref="SupportedColorSpaces"/>
422 public ColorSpaceTransform(ColorSpace colorSpace)
424 ColorSpace = colorSpace;
428 /// Gets or sets the colorspace of the result image.
430 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
431 /// <exception cref="NotSupportedException"><paramref name="value"/> is not supported.</exception>
432 /// <seealso cref="SupportedColorSpaces"/>
433 public ColorSpace ColorSpace
435 get { return _imageColorSpace.ToCommonColorSpace(); }
438 ValidationUtil.ValidateEnum(typeof(ColorSpace), value, nameof(ColorSpace));
440 _imageColorSpace = value.ToImageColorSpace();
444 internal override void Configure(TransformHandle handle)
446 SetColorspace(handle, _imageColorSpace);
450 /// Gets the supported colorspaces for <see cref="ColorSpaceTransform"/>.
452 public static IEnumerable<ColorSpace> SupportedColorSpaces
456 foreach (ImageColorSpace value in Enum.GetValues(typeof(ImageColorSpace)))
458 yield return value.ToCommonColorSpace();
467 public class CropTransform : ImageTransform
469 private Rectangle _region;
472 /// Initializes a new instance of the <see cref="CropTransform"/> class.
474 /// <param name="region">The crop region.</param>
475 /// <exception cref="ArgumentOutOfRangeException">
476 /// The X-position of <paramref name="region"/> is less than zero.<br/>
478 /// The Y-position of <paramref name="region"/> is less than zero.<br/>
480 /// The width of <paramref name="region"/> is less than or equal to zero.<br/>
482 /// The height of <paramref name="region"/> is less than or equal to zero.
484 public CropTransform(Rectangle region)
490 /// Gets or sets the crop region.
492 /// <exception cref="ArgumentOutOfRangeException">
493 /// The X-position of <paramref name="value"/> is less than zero.<br/>
495 /// The Y-position of <paramref name="value"/> is less than zero.<br/>
497 /// The width of <paramref name="value"/> is less than or equal to zero.<br/>
499 /// The height of <paramref name="value"/> is less than or equal to zero.
501 public Rectangle Region
503 get { return _region; }
509 throw new ArgumentOutOfRangeException(nameof(Region), value,
510 "X position of the region can't be less than zero.");
515 throw new ArgumentOutOfRangeException(nameof(Region), value,
516 "Y position of the region can't be less than zero.");
519 if (value.Width <= 0)
521 throw new ArgumentOutOfRangeException(nameof(Region), value,
522 "Width of the region can't be less than or equal zero.");
525 if (value.Height < 0)
527 throw new ArgumentOutOfRangeException(nameof(Region), value,
528 "Height of the region can't be less than or equal to zero.");
535 internal override void Configure(TransformHandle handle)
537 SetCropArea(handle, Region.Left, Region.Top, Region.Right, Region.Bottom);
542 /// Resizes an image.
544 public class ResizeTransform : ImageTransform
549 /// Initializes a new instance of the <see cref="ResizeTransform"/> class.
551 /// <param name="size">The size that an image is resized to.</param>
552 /// <exception cref="ArgumentOutOfRangeException">
553 /// The width of <paramref name="size"/> is less than or equal to zero.<br/>
555 /// The height of <paramref name="size"/> is less than or equal to zero.
557 public ResizeTransform(Size size)
563 /// Gets or sets the size that an image is resized to.
565 /// <exception cref="ArgumentOutOfRangeException">
566 /// The width of <paramref name="value"/> is less than or equal to zero.<br/>
568 /// The height of <paramref name="value"/> is less than or equal to zero.
572 get { return _size; }
575 if (value.Width <= 0)
577 throw new ArgumentOutOfRangeException(nameof(Size), value,
578 "Width of the size can't be less than or equal to zero.");
581 if (value.Height <= 0)
583 throw new ArgumentOutOfRangeException(nameof(Size), value,
584 "Height of the size can't be less than or equal to zero.");
591 internal override void Configure(TransformHandle handle)
593 SetResolution(handle, (uint)Size.Width, (uint)Size.Height);