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>();
119 IntPtr outBuffer = IntPtr.Zero;
123 Unmanaged.SetOutputBuffer(Handle, out outBuffer).ThrowIfFailed("Failed to initialize encoder");
126 Unmanaged.Run(Handle, out size).ThrowIfFailed("Failed to encode given image");
128 byte[] buf = new byte[size];
129 Marshal.Copy(outBuffer, buf, 0, (int)size);
130 outStream.Write(buf, 0, (int)size);
132 tcs.TrySetResult(true);
136 tcs.TrySetException(e);
140 Interop.Libc.Free(outBuffer);
147 internal Task EncodeAsync(Action<ImageEncoderHandle> settingInputAction, Stream outStream)
149 Debug.Assert(settingInputAction != null);
151 if (outStream == null)
153 throw new ArgumentNullException(nameof(outStream));
156 if (outStream.CanWrite == false)
158 throw new ArgumentException("The stream is not writable.", nameof(outStream));
163 settingInputAction(Handle);
165 return Run(outStream);
169 /// Encodes an image from a raw image buffer to a specified <see cref="Stream"/>.
171 /// <param name="inputBuffer">The image buffer to encode.</param>
172 /// <param name="outStream">The stream that the image is encoded to.</param>
173 /// <returns>A task that represents the asynchronous encoding operation.</returns>
174 /// <exception cref="ArgumentNullException">
175 /// <paramref name="inputBuffer"/> is null.<br/>
177 /// <paramref name="outStream"/> is null.
179 /// <exception cref="ArgumentException">
180 /// <paramref name="inputBuffer"/> is an empty array.<br/>
182 /// <paramref name="outStream"/> is not writable.
184 /// <exception cref="InvalidOperationException">The resolution is not set.</exception>
185 /// <exception cref="ObjectDisposedException">The <see cref="ImageEncoder"/> has already been disposed of.</exception>
186 /// <seealso cref="SetResolution"/>
187 public Task EncodeAsync(byte[] inputBuffer, Stream outStream)
189 if (inputBuffer == null)
191 throw new ArgumentNullException(nameof(inputBuffer));
194 if (inputBuffer.Length == 0)
196 throw new ArgumentException("buffer is empty.", nameof(inputBuffer));
199 return EncodeAsync(handle =>
201 Unmanaged.SetInputBuffer(handle, inputBuffer).
202 ThrowIfFailed("Failed to configure encoder; InputBuffer");
206 internal void Initialize()
210 if (_hasResolution == false)
212 throw new InvalidOperationException("Resolution is not set.");
216 internal abstract void Configure(ImageEncoderHandle handle);
218 #region IDisposable Support
219 private bool _disposed = false;
222 /// Releases the unmanaged resources used by the ImageEncoder.
224 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
225 protected virtual void Dispose(bool disposing)
238 /// Releases all resources used by the ImageEncoder.
240 public void Dispose()
248 /// Provides the ability to encode the Bitmap (BMP) format images.
250 public class BmpEncoder : ImageEncoder
253 /// Initializes a new instance of the <see cref="BmpEncoder"/> class.
255 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.</remarks>
256 public BmpEncoder() : base(ImageFormat.Bmp)
260 internal override void Configure(ImageEncoderHandle handle)
266 /// Provides the ability to encode the Portable Network Graphics (PNG) format images.
268 public class PngEncoder : ImageEncoder
271 /// A read-only field that represents the default value of <see cref="Compression"/>.
273 public static readonly PngCompression DefaultCompression = PngCompression.Level6;
275 private PngCompression? _compression;
278 /// Initializes a new instance of the <see cref="PngEncoder"/> class.
280 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
281 public PngEncoder() :
282 base(ImageFormat.Png)
287 /// Initializes a new instance of the <see cref="PngEncoder"/> class with <see cref="PngCompression"/>.
289 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
290 /// <param name="compression">The compression level of the encoder.</param>
291 /// <exception cref="ArgumentException"><paramref name="compression"/> is invalid.</exception>
292 public PngEncoder(PngCompression compression) :
293 base(ImageFormat.Png)
295 Compression = compression;
299 /// Gets or sets the compression level of the png image.
301 /// <value>The compression level. The default is <see cref="PngCompression.Level6"/>.</value>
302 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
303 public PngCompression Compression
305 get { return _compression ?? DefaultCompression; }
308 ValidationUtil.ValidateEnum(typeof(PngCompression), value, nameof(Compression));
310 _compression = value;
314 internal override void Configure(ImageEncoderHandle handle)
316 if (_compression.HasValue)
318 Unmanaged.SetPngCompression(handle, _compression.Value).
319 ThrowIfFailed("Failed to configure encoder; PngCompression");
325 /// Provides the ability to encode the Joint Photographic Experts Group (JPEG) format images.
327 public class JpegEncoder : ImageEncoder
330 /// A read-only field that represents the default value of <see cref="Quality"/>.
332 public static readonly int DefaultQuality = 75;
334 private int? _quality;
337 /// Initializes a new instance of the <see cref="JpegEncoder"/> class.
339 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
340 public JpegEncoder() : base(ImageFormat.Jpeg)
346 /// Initializes a new instance of the <see cref="JpegEncoder"/> class with initial quality value.
348 /// <param name="quality">The quality for JPEG image encoding; from 1(lowest quality) to 100(highest quality).</param>
349 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
350 /// <exception cref="ArgumentOutOfRangeException">
351 /// <paramref name="quality"/> is less than or equal to 0.<br/>
353 /// <paramref name="quality"/> is greater than 100.
355 public JpegEncoder(int quality) :
356 base(ImageFormat.Jpeg)
362 /// Gets or sets the quality of the encoded image.
365 /// The quality of the output image. The default is 75.<br/>
366 /// Valid value is from 1(lowest quality) to 100(highest quality).
368 /// <exception cref="ArgumentOutOfRangeException">
369 /// <paramref name="value"/> is less than or equal to 0.<br/>
371 /// <paramref name="value"/> is greater than 100.
375 get { return _quality ?? DefaultQuality; }
378 if (value <= 0 || value > 100)
380 throw new ArgumentOutOfRangeException(nameof(Quality), value,
381 "Valid range is from 1 to 100, inclusive.");
387 internal override void Configure(ImageEncoderHandle handle)
389 if (_quality.HasValue)
391 Unmanaged.SetQuality(handle, _quality.Value).
392 ThrowIfFailed("Failed to configure encoder; Quality");
398 /// Provides the ability to encode the Graphics Interchange Format (GIF) format images.
400 public class GifEncoder : ImageEncoder
403 /// Initializes a new instance of the <see cref="GifEncoder"/> class.
405 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Gif"/>.</remarks>
406 public GifEncoder() : base(ImageFormat.Gif)
410 internal override void Configure(ImageEncoderHandle handle)
415 /// Encodes a Graphics Interchange Format (GIF) image from multiple raw image buffers to a specified <see cref="Stream"/>.
417 /// <param name="frames">The image frames to encode.</param>
418 /// <param name="outStream">The stream that the image is encoded to.</param>
419 /// <returns>A task that represents the asynchronous encoding operation.</returns>
420 /// <exception cref="ArgumentNullException">
421 /// <paramref name="frames"/> is null.<br/>
423 /// <paramref name="outStream"/> is null.
425 /// <exception cref="ArgumentException">
426 /// <paramref name="frames"/> has no element(empty).<br/>
428 /// <paramref name="outStream"/> is not writable.
430 /// <exception cref="InvalidOperationException">The resolution is not set.</exception>
431 /// <exception cref="ObjectDisposedException">The <see cref="ImageEncoder"/> has already been disposed of.</exception>
432 /// <seealso cref="ImageEncoder.SetResolution"/>
433 public Task EncodeAsync(IEnumerable<GifFrame> frames, Stream outStream)
437 throw new ArgumentNullException(nameof(frames));
440 if (frames.Count() == 0)
442 throw new ArgumentException("frames is a empty collection", nameof(frames));
445 return EncodeAsync(handle =>
447 foreach (GifFrame frame in frames)
451 throw new ArgumentNullException(nameof(frames));
453 Unmanaged.SetInputBuffer(handle, frame.Buffer).
454 ThrowIfFailed("Failed to configure encoder; Buffer");
456 Unmanaged.SetGifFrameDelayTime(handle, (ulong)frame.Delay).
457 ThrowIfFailed("Failed to configure encoder; Delay");