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.Generic;
19 using System.Diagnostics;
22 using System.Runtime.InteropServices;
23 using System.Threading.Tasks;
24 using static Interop.ImageUtil;
25 using Unmanaged = Interop.ImageUtil.Encode;
27 namespace Tizen.Multimedia.Util
30 /// This is a base class for image encoders.
32 public abstract class ImageEncoder : IDisposable
34 private ImageEncoderHandle _handle;
36 private bool _hasResolution;
38 internal ImageEncoder(ImageFormat format)
40 Unmanaged.Create(format, out _handle).ThrowIfFailed("Failed to create ImageEncoder");
42 Debug.Assert(_handle != null);
44 OutputFormat = format;
47 private ImageEncoderHandle Handle
53 throw new ObjectDisposedException(GetType().Name);
61 /// Gets the image format of this encoder.
63 public ImageFormat OutputFormat { get; }
66 /// Sets the resolution of the output image.
68 /// <param name="resolution">The target resolution.</param>
69 /// <exception cref="ArgumentOutOfRangeException">
70 /// The width of <paramref name="resolution"/> is less than or equal to zero.<br/>
72 /// The height of <paramref name="resolution"/> is less than or equal to zero.
74 public void SetResolution(Size resolution)
76 if (resolution.Width <= 0)
78 throw new ArgumentOutOfRangeException(nameof(resolution), resolution.Width,
79 "The width of resolution can't be less than or equal to zero.");
81 if (resolution.Height <= 0)
83 throw new ArgumentOutOfRangeException(nameof(resolution), resolution.Height,
84 "The height of resolution can't be less than or equal to zero.");
87 Unmanaged.SetResolution(Handle, (uint)resolution.Width, (uint)resolution.Height).
88 ThrowIfFailed("Failed to set the resolution");
90 _hasResolution = true;
94 /// Sets the color-space of the output image.
96 /// <param name="colorSpace">The target color-space.</param>
97 /// <exception cref="ArgumentException"><paramref name="colorSpace"/> is invalid.</exception>
98 /// <exception cref="NotSupportedException"><paramref name="colorSpace"/> is not supported by the encoder.</exception>
99 /// <seealso cref="ImageUtil.GetSupportedColorSpaces(ImageFormat)"/>
100 public void SetColorSpace(ColorSpace colorSpace)
102 ValidationUtil.ValidateEnum(typeof(ColorSpace), colorSpace, nameof(colorSpace));
104 if (ImageUtil.GetSupportedColorSpaces(OutputFormat).Contains(colorSpace) == false)
106 throw new NotSupportedException($"{colorSpace.ToString()} is not supported for {OutputFormat}.");
109 Unmanaged.SetColorspace(Handle, colorSpace.ToImageColorSpace()).
110 ThrowIfFailed("Failed to set the color space");
113 private Task Run(Stream outStream)
115 var tcs = new TaskCompletionSource<bool>();
117 IntPtr outBuffer = IntPtr.Zero;
118 Unmanaged.SetOutputBuffer(Handle, out outBuffer).ThrowIfFailed("Failed to initialize encoder");
125 Unmanaged.Run(Handle, out size).ThrowIfFailed("Failed to encode given image");
127 byte[] buf = new byte[size];
128 Marshal.Copy(outBuffer, buf, 0, (int)size);
129 outStream.Write(buf, 0, (int)size);
131 tcs.TrySetResult(true);
135 tcs.TrySetException(e);
139 Interop.Libc.Free(outBuffer);
146 internal Task EncodeAsync(Action<ImageEncoderHandle> settingInputAction, Stream outStream)
148 Debug.Assert(settingInputAction != null);
150 if (outStream == null)
152 throw new ArgumentNullException(nameof(outStream));
155 if (outStream.CanWrite == false)
157 throw new ArgumentException("The stream is not writable.", nameof(outStream));
162 settingInputAction(Handle);
164 return Run(outStream);
168 /// Encodes an image from a raw image buffer to a specified <see cref="Stream"/>.
170 /// <param name="inputBuffer">The image buffer to encode.</param>
171 /// <param name="outStream">The stream that the image is encoded to.</param>
172 /// <returns>A task that represents the asynchronous encoding operation.</returns>
173 /// <exception cref="ArgumentNullException">
174 /// <paramref name="inputBuffer"/> is null.<br/>
176 /// <paramref name="outStream"/> is null.
178 /// <exception cref="ArgumentException">
179 /// <paramref name="inputBuffer"/> is an empty array.<br/>
181 /// <paramref name="outStream"/> is not writable.
183 /// <exception cref="InvalidOperationException">The resolution is not set.</exception>
184 /// <exception cref="ObjectDisposedException">The <see cref="ImageEncoder"/> has already been disposed of.</exception>
185 /// <seealso cref="SetResolution"/>
186 public Task EncodeAsync(byte[] inputBuffer, Stream outStream)
188 if (inputBuffer == null)
190 throw new ArgumentNullException(nameof(inputBuffer));
193 if (inputBuffer.Length == 0)
195 throw new ArgumentException("buffer is empty.", nameof(inputBuffer));
198 return EncodeAsync(handle =>
200 Unmanaged.SetInputBuffer(handle, inputBuffer).
201 ThrowIfFailed("Failed to configure encoder; InputBuffer");
205 internal void Initialize()
209 if (_hasResolution == false)
211 throw new InvalidOperationException("Resolution is not set.");
215 internal abstract void Configure(ImageEncoderHandle handle);
217 #region IDisposable Support
218 private bool _disposed = false;
221 /// Releases the unmanaged resources used by the ImageEncoder.
223 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
224 protected virtual void Dispose(bool disposing)
237 /// Releases all resources used by the ImageEncoder.
239 public void Dispose()
247 /// Provides the ability to encode the Bitmap (BMP) format images.
249 public class BmpEncoder : ImageEncoder
252 /// Initializes a new instance of the <see cref="BmpEncoder"/> class.
254 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.</remarks>
255 public BmpEncoder() : base(ImageFormat.Bmp)
259 internal override void Configure(ImageEncoderHandle handle)
265 /// Provides the ability to encode the Portable Network Graphics (PNG) format images.
267 public class PngEncoder : ImageEncoder
270 /// A read-only field that represents the default value of <see cref="Compression"/>.
272 public static readonly PngCompression DefaultCompression = PngCompression.Level6;
274 private PngCompression? _compression;
277 /// Initializes a new instance of the <see cref="PngEncoder"/> class.
279 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
280 public PngEncoder() :
281 base(ImageFormat.Png)
286 /// Initializes a new instance of the <see cref="PngEncoder"/> class with <see cref="PngCompression"/>.
288 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
289 /// <param name="compression">The compression level of the encoder.</param>
290 /// <exception cref="ArgumentException"><paramref name="compression"/> is invalid.</exception>
291 public PngEncoder(PngCompression compression) :
292 base(ImageFormat.Png)
294 Compression = compression;
298 /// Gets or sets the compression level of the png image.
300 /// <value>The compression level. The default is <see cref="PngCompression.Level6"/>.</value>
301 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
302 public PngCompression Compression
304 get { return _compression ?? DefaultCompression; }
307 ValidationUtil.ValidateEnum(typeof(PngCompression), value, nameof(Compression));
309 _compression = value;
313 internal override void Configure(ImageEncoderHandle handle)
315 if (_compression.HasValue)
317 Unmanaged.SetPngCompression(handle, _compression.Value).
318 ThrowIfFailed("Failed to configure encoder; PngCompression");
324 /// Provides the ability to encode the Joint Photographic Experts Group (JPEG) format images.
326 public class JpegEncoder : ImageEncoder
329 /// A read-only field that represents the default value of <see cref="Quality"/>.
331 public static readonly int DefaultQuality = 75;
333 private int? _quality;
336 /// Initializes a new instance of the <see cref="JpegEncoder"/> class.
338 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
339 public JpegEncoder() : base(ImageFormat.Jpeg)
345 /// Initializes a new instance of the <see cref="JpegEncoder"/> class with initial quality value.
347 /// <param name="quality">The quality for JPEG image encoding; from 1(lowest quality) to 100(highest quality).</param>
348 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
349 /// <exception cref="ArgumentOutOfRangeException">
350 /// <paramref name="quality"/> is less than or equal to 0.<br/>
352 /// <paramref name="quality"/> is greater than 100.
354 public JpegEncoder(int quality) :
355 base(ImageFormat.Jpeg)
361 /// Gets or sets the quality of the encoded image.
364 /// The quality of the output image. The default is 75.<br/>
365 /// Valid value is from 1(lowest quality) to 100(highest quality).
367 /// <exception cref="ArgumentOutOfRangeException">
368 /// <paramref name="value"/> is less than or equal to 0.<br/>
370 /// <paramref name="value"/> is greater than 100.
374 get { return _quality ?? DefaultQuality; }
377 if (value <= 0 || value > 100)
379 throw new ArgumentOutOfRangeException(nameof(Quality), value,
380 "Valid range is from 1 to 100, inclusive.");
386 internal override void Configure(ImageEncoderHandle handle)
388 if (_quality.HasValue)
390 Unmanaged.SetQuality(handle, _quality.Value).
391 ThrowIfFailed("Failed to configure encoder; Quality");
397 /// Provides the ability to encode the Graphics Interchange Format (GIF) format images.
399 public class GifEncoder : ImageEncoder
402 /// Initializes a new instance of the <see cref="GifEncoder"/> class.
404 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Gif"/>.</remarks>
405 public GifEncoder() : base(ImageFormat.Gif)
409 internal override void Configure(ImageEncoderHandle handle)
414 /// Encodes a Graphics Interchange Format (GIF) image from multiple raw image buffers to a specified <see cref="Stream"/>.
416 /// <param name="frames">The image frames to encode.</param>
417 /// <param name="outStream">The stream that the image is encoded to.</param>
418 /// <returns>A task that represents the asynchronous encoding operation.</returns>
419 /// <exception cref="ArgumentNullException">
420 /// <paramref name="frames"/> is null.<br/>
422 /// <paramref name="outStream"/> is null.
424 /// <exception cref="ArgumentException">
425 /// <paramref name="frames"/> has no element(empty).<br/>
427 /// <paramref name="outStream"/> is not writable.
429 /// <exception cref="InvalidOperationException">The resolution is not set.</exception>
430 /// <exception cref="ObjectDisposedException">The <see cref="ImageEncoder"/> has already been disposed of.</exception>
431 /// <seealso cref="ImageEncoder.SetResolution"/>
432 public Task EncodeAsync(IEnumerable<GifFrame> frames, Stream outStream)
436 throw new ArgumentNullException(nameof(frames));
439 if (frames.Count() == 0)
441 throw new ArgumentException("frames is a empty collection", nameof(frames));
444 return EncodeAsync(handle =>
446 foreach (GifFrame frame in frames)
450 throw new ArgumentNullException(nameof(frames));
452 Unmanaged.SetInputBuffer(handle, frame.Buffer).
453 ThrowIfFailed("Failed to configure encoder; Buffer");
455 Unmanaged.SetGifFrameDelayTime(handle, (ulong)frame.Delay).
456 ThrowIfFailed("Failed to configure encoder; Delay");