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;
24 using System.Threading.Tasks;
26 using NativeEncoder = Interop.ImageUtil.Encode;
27 using NativeUtil = Interop.ImageUtil;
29 namespace Tizen.Multimedia.Util
32 /// This is a base class for image encoders.
34 /// <since_tizen> 4 </since_tizen>
35 public abstract class ImageEncoder : IDisposable
37 private ImageEncoderHandle _handle;
38 internal IntPtr _imageUtilHandle;
39 internal IntPtr _animationHandle;
41 internal Size? _resolution;
42 internal ColorSpace? _colorSpace;
44 internal ImageEncoder(ImageFormat format)
46 NativeEncoder.Create(format, out _handle).ThrowIfFailed("Failed to create ImageEncoder");
48 Debug.Assert(_handle != null);
50 OutputFormat = format;
53 private ImageEncoderHandle Handle
59 throw new ObjectDisposedException(GetType().Name);
67 /// Gets the image format of this encoder.
69 /// <since_tizen> 4 </since_tizen>
70 public ImageFormat OutputFormat { get; }
73 /// Sets the resolution of the output image.
75 /// <param name="resolution">The target resolution.</param>
76 /// <exception cref="ArgumentOutOfRangeException">
77 /// The width of <paramref name="resolution"/> is less than or equal to zero.<br/>
79 /// The height of <paramref name="resolution"/> is less than or equal to zero.
81 /// <since_tizen> 4 </since_tizen>
82 public void SetResolution(Size resolution)
84 if (resolution.Width <= 0)
86 throw new ArgumentOutOfRangeException(nameof(resolution), resolution.Width,
87 "The width of resolution can't be less than or equal to zero.");
89 if (resolution.Height <= 0)
91 throw new ArgumentOutOfRangeException(nameof(resolution), resolution.Height,
92 "The height of resolution can't be less than or equal to zero.");
95 _resolution = resolution;
99 /// Sets the color-space of the output image.
101 /// <param name="colorSpace">The target color-space.</param>
102 /// <value>The default color space is <see cref="ColorSpace.Rgba8888"/>
103 /// <exception cref="ArgumentException"><paramref name="colorSpace"/> is invalid.</exception>
104 /// <exception cref="NotSupportedException"><paramref name="colorSpace"/> is not supported by the encoder.</exception>
105 /// <seealso cref="ImageUtil.GetSupportedColorSpaces(ImageFormat)"/>
106 /// <since_tizen> 4 </since_tizen>
107 public void SetColorSpace(ColorSpace colorSpace)
109 ValidationUtil.ValidateEnum(typeof(ColorSpace), colorSpace, nameof(colorSpace));
111 if (ImageUtil.GetSupportedColorSpaces(OutputFormat).Contains(colorSpace) == false)
113 throw new NotSupportedException($"{colorSpace.ToString()} is not supported for {OutputFormat}.");
116 _colorSpace = colorSpace;
119 internal virtual void RunEncoding(object outStream)
121 IntPtr outBuffer = IntPtr.Zero;
126 NativeEncoder.RunToBuffer(Handle, _imageUtilHandle, out outBuffer, out size).
127 ThrowIfFailed("Failed to encode given image");
129 byte[] buf = new byte[size];
130 Marshal.Copy(outBuffer, buf, 0, (int)size);
131 (outStream as Stream).Write(buf, 0, (int)size);
135 Marshal.FreeHGlobal(outBuffer);
136 NativeUtil.Destroy(_imageUtilHandle).ThrowIfFailed("Failed to destroy ImageUtil handle");
140 private Task Run(Stream outStream)
142 return Task.Factory.StartNew(RunEncoding, outStream, CancellationToken.None,
143 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
144 TaskScheduler.Default);
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 /// <since_tizen> 4 </since_tizen>
188 public Task EncodeAsync(byte[] inputBuffer, Stream outStream)
190 if (inputBuffer == null)
192 throw new ArgumentNullException(nameof(inputBuffer));
195 if (inputBuffer.Length == 0)
197 throw new ArgumentException("buffer is empty.", nameof(inputBuffer));
200 return EncodeAsync(handle => {
201 NativeUtil.Create((uint)_resolution.Value.Width, (uint)_resolution.Value.Height, _colorSpace.Value.ToImageColorSpace(),
202 inputBuffer, inputBuffer.Length, out _imageUtilHandle).ThrowIfFailed("Failed to create ImageUtil handle");
206 internal void Initialize()
210 if (!_resolution.HasValue)
212 throw new InvalidOperationException("Resolution is not set.");
214 if (!_colorSpace.HasValue)
216 _colorSpace = ColorSpace.Rgba8888;
217 Log.Info("Tizen.Multimedia.Util", "ColorSpace was set to default value(Rgba8888).");
221 internal abstract void Configure(ImageEncoderHandle handle);
223 #region IDisposable Support
224 private bool _disposed = false;
227 /// Releases the unmanaged resources used by the ImageEncoder.
229 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
230 /// <since_tizen> 4 </since_tizen>
231 protected virtual void Dispose(bool disposing)
244 /// Releases all resources used by the ImageEncoder.
246 /// <since_tizen> 4 </since_tizen>
247 public void Dispose()
255 /// Provides the ability to encode the Bitmap (BMP) format images.
257 /// <since_tizen> 4 </since_tizen>
258 public class BmpEncoder : ImageEncoder
261 /// Initializes a new instance of the <see cref="BmpEncoder"/> class.
263 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.</remarks>
264 /// <since_tizen> 4 </since_tizen>
265 public BmpEncoder() : base(ImageFormat.Bmp)
269 internal override void Configure(ImageEncoderHandle handle)
275 /// Provides the ability to encode the Portable Network Graphics (PNG) format images.
277 /// <since_tizen> 4 </since_tizen>
278 public class PngEncoder : ImageEncoder
281 /// A read-only field that represents the default value of <see cref="Compression"/>.
283 /// <since_tizen> 4 </since_tizen>
284 public static readonly PngCompression DefaultCompression = PngCompression.Level6;
286 private PngCompression? _compression;
289 /// Initializes a new instance of the <see cref="PngEncoder"/> class.
291 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
292 /// <since_tizen> 4 </since_tizen>
293 public PngEncoder() :
294 base(ImageFormat.Png)
299 /// Initializes a new instance of the <see cref="PngEncoder"/> class with <see cref="PngCompression"/>.
301 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
302 /// <param name="compression">The compression level of the encoder.</param>
303 /// <exception cref="ArgumentException"><paramref name="compression"/> is invalid.</exception>
304 /// <since_tizen> 4 </since_tizen>
305 public PngEncoder(PngCompression compression) :
306 base(ImageFormat.Png)
308 Compression = compression;
312 /// Gets or sets the compression level of the png image.
314 /// <value>The compression level. The default is <see cref="PngCompression.Level6"/>.</value>
315 /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
316 /// <since_tizen> 4 </since_tizen>
317 public PngCompression Compression
319 get { return _compression ?? DefaultCompression; }
322 ValidationUtil.ValidateEnum(typeof(PngCompression), value, nameof(Compression));
324 _compression = value;
328 internal override void Configure(ImageEncoderHandle handle)
330 if (_compression.HasValue)
332 NativeEncoder.SetPngCompression(handle, _compression.Value).
333 ThrowIfFailed("Failed to configure encoder; PngCompression");
339 /// Provides the ability to encode the Joint Photographic Experts Group (JPEG) format images.
341 /// <since_tizen> 4 </since_tizen>
342 public class JpegEncoder : ImageEncoder
345 /// A read-only field that represents the default value of <see cref="Quality"/>.
347 /// <since_tizen> 4 </since_tizen>
348 public static readonly int DefaultQuality = 75;
350 private int? _quality;
353 /// Initializes a new instance of the <see cref="JpegEncoder"/> class.
355 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
356 /// <since_tizen> 4 </since_tizen>
357 public JpegEncoder() : base(ImageFormat.Jpeg)
363 /// Initializes a new instance of the <see cref="JpegEncoder"/> class with initial quality value.
365 /// <param name="quality">The quality for JPEG image encoding; from 1(lowest quality) to 100(highest quality).</param>
366 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
367 /// <exception cref="ArgumentOutOfRangeException">
368 /// <paramref name="quality"/> is less than or equal to 0.<br/>
370 /// <paramref name="quality"/> is greater than 100.
372 /// <since_tizen> 4 </since_tizen>
373 public JpegEncoder(int quality) :
374 base(ImageFormat.Jpeg)
380 /// Gets or sets the quality of the encoded image.
383 /// The quality of the output image. The default is 75.<br/>
384 /// Valid value is from 1(lowest quality) to 100(highest quality).
386 /// <exception cref="ArgumentOutOfRangeException">
387 /// <paramref name="value"/> is less than or equal to 0.<br/>
389 /// <paramref name="value"/> is greater than 100.
391 /// <since_tizen> 4 </since_tizen>
394 get { return _quality ?? DefaultQuality; }
397 if (value <= 0 || value > 100)
399 throw new ArgumentOutOfRangeException(nameof(Quality), value,
400 "Valid range is from 1 to 100, inclusive.");
406 internal override void Configure(ImageEncoderHandle handle)
408 if (_quality.HasValue)
410 NativeEncoder.SetQuality(handle, _quality.Value).
411 ThrowIfFailed("Failed to configure encoder; Quality");
417 /// Provides the ability to encode the Graphics Interchange Format (GIF) format images.
419 /// <since_tizen> 4 </since_tizen>
420 public class GifEncoder : ImageEncoder
422 private IEnumerable<GifFrame> _frames;
425 /// Initializes a new instance of the <see cref="GifEncoder"/> class.
427 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.Gif"/>.</remarks>
428 /// <since_tizen> 4 </since_tizen>
429 public GifEncoder() : base(ImageFormat.Gif)
433 internal override void Configure(ImageEncoderHandle handle)
438 /// Encodes a Graphics Interchange Format (GIF) image from multiple raw image buffers to a specified <see cref="Stream"/>.
440 /// <param name="frames">The image frames to encode.</param>
441 /// <param name="outStream">The stream that the image is encoded to.</param>
442 /// <returns>A task that represents the asynchronous encoding operation.</returns>
443 /// <exception cref="ArgumentNullException">
444 /// <paramref name="frames"/> is null.<br/>
446 /// <paramref name="outStream"/> is null.
448 /// <exception cref="ArgumentException">
449 /// <paramref name="frames"/> has no element(empty).<br/>
451 /// <paramref name="outStream"/> is not writable.
453 /// <exception cref="InvalidOperationException">The resolution is not set.</exception>
454 /// <exception cref="ObjectDisposedException">The <see cref="ImageEncoder"/> has already been disposed of.</exception>
455 /// <seealso cref="ImageEncoder.SetResolution"/>
456 /// <since_tizen> 4 </since_tizen>
457 public Task EncodeAsync(IEnumerable<GifFrame> frames, Stream outStream)
461 throw new ArgumentNullException(nameof(frames));
466 throw new ArgumentException("frames is a empty collection", nameof(frames));
471 return EncodeAsync(handle => {}, outStream);
474 internal override void RunEncoding(object outStream)
476 IntPtr outBuffer = IntPtr.Zero;
478 NativeEncoder.AnimationCreate(AnimationType.Gif, out _animationHandle).
479 ThrowIfFailed("Failed to create animation handle");
483 foreach (GifFrame frame in _frames)
487 throw new ArgumentNullException(nameof(frame));
490 NativeUtil.Create((uint)_resolution.Value.Width, (uint)_resolution.Value.Height, _colorSpace.Value.ToImageColorSpace(),
491 frame.Buffer, frame.Buffer.Length, out _imageUtilHandle).ThrowIfFailed("Failed to create ImageUtil handle");
492 NativeEncoder.AnimationAddFrame(_animationHandle, _imageUtilHandle, frame.Delay).
493 ThrowIfFailed("Failed to add frame");
495 NativeUtil.Destroy(_imageUtilHandle).ThrowIfFailed("Failed to destroy ImageUtil handle");
498 NativeEncoder.AnimationSaveToBuffer(_animationHandle, out outBuffer, out ulong size).
499 ThrowIfFailed("Failed to encode given image");
501 byte[] buf = new byte[size];
502 Marshal.Copy(outBuffer, buf, 0, (int)size);
503 (outStream as Stream).Write(buf, 0, (int)size);
507 Marshal.FreeHGlobal(outBuffer);
508 if (_animationHandle != IntPtr.Zero)
510 NativeEncoder.AnimationDestroy(_animationHandle).ThrowIfFailed("Failed to destroy animation handle");
517 /// Provides the ability to encode the WebP (Lossless and lossy compression for images on the web) format images.
519 /// <since_tizen> 8 </since_tizen>
520 public class WebPEncoder : ImageEncoder
523 /// Initializes a new instance of the <see cref="WebPEncoder"/> class.
525 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.WebP"/>.</remarks>
526 /// <since_tizen> 8 </since_tizen>
527 public WebPEncoder() :
528 base(ImageFormat.WebP)
533 /// Initializes a new instance of the <see cref="WebPEncoder"/> class with the information for lossless or lossy compression.
535 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.WebP"/>.</remarks>
536 /// <param name="lossless">
537 /// The flag determining whether the compression is lossless or lossy: true for lossless, false for lossy.<br/>
538 /// The default value is false.
540 /// <since_tizen> 8 </since_tizen>
541 public WebPEncoder(bool lossless) :
542 base(ImageFormat.WebP)
548 /// Gets or sets the lossless or lossy WebP compression.
551 /// The property determining whether the WebP compression is lossless or lossy.<br/>
552 /// The default is false(lossy).</value>
553 /// <since_tizen> 8 </since_tizen>
554 public bool Lossless { get; set; } = false;
556 internal override void Configure(ImageEncoderHandle handle)
558 NativeEncoder.SetLossless(handle, Lossless).
559 ThrowIfFailed("Failed to configure encoder; Lossless");
564 /// Provides the ability to encode the JPEG(Joint Photographic Experts Group) XL format images.
566 /// <since_tizen> 10 </since_tizen>
567 public class JpegXlEncoder : ImageEncoder
570 /// Initializes a new instance of the <see cref="JpegXlEncoder"/> class.
572 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.JpegXl"/>.</remarks>
573 /// <since_tizen> 10 </since_tizen>
574 public JpegXlEncoder() :
575 base(ImageFormat.JpegXl)
580 /// Initializes a new instance of the <see cref="JpegXlEncoder"/> class for lossless or lossy compression.
582 /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.JpegXl"/>.</remarks>
583 /// <param name="lossless">
584 /// The flag determining whether the compression is lossless or lossy: true for lossless, false for lossy.<br/>
585 /// The default value is false.
587 /// <since_tizen> 10 </since_tizen>
588 public JpegXlEncoder(bool lossless) :
589 base(ImageFormat.JpegXl)
595 /// Gets or sets the lossless or lossy JpegXl compression.
598 /// The property determining whether the JpegXl compression is lossless or lossy.<br/>
599 /// The default is false(lossy).</value>
600 /// <since_tizen> 10 </since_tizen>
601 public bool Lossless { get; set; } = false;
603 internal override void Configure(ImageEncoderHandle handle)
605 NativeEncoder.SetLossless(handle, Lossless).
606 ThrowIfFailed($"Failed to configure encoder; Lossless={Lossless}");