/* * 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.IO; using System.Threading; using System.Threading.Tasks; namespace ElmSharp { /// /// The Image is a widget that allows one to load and display an image file on it, /// be it from a disk file or from a memory region. /// Inherits Widget. /// /// preview public class Image : Widget { bool _canScaleUp = true; bool _canScaleDown = true; SmartEvent _clicked; Color _color = Color.Default; EvasImage _imageObject = null; /// /// Creates and initializes a new instance of the Image class. /// /// The parent is a given container, which will be attached by the image as a child. It's type. /// preview public Image(EvasObject parent) : base(parent) { _clicked = new SmartEvent(this, "clicked"); _clicked.On += (s, e) => Clicked?.Invoke(this, EventArgs.Empty); } /// /// Clicked will be triggered when the image is clicked. /// /// preview public event EventHandler Clicked; /// /// LoadingCompleted will be triggered when the image is loaded completely. /// /// preview public event EventHandler LoadingCompleted; /// /// Clicked will be triggered when the image fails to load. /// /// preview public event EventHandler LoadingFailed; /// /// Gets the file that is used as an image. /// /// preview public string File { get { return Interop.Elementary.elm_image_file_get(RealHandle); } } /// /// Sets or gets the smooth effect for an image. /// /// preview public bool IsSmooth { get { return Interop.Elementary.elm_image_smooth_get(RealHandle); } set { Interop.Elementary.elm_image_smooth_set(RealHandle, value); } } /// /// Sets or gets whether scaling is disabled on the object. /// /// preview public bool IsScaling { get { return !Interop.Elementary.elm_image_no_scale_get(RealHandle); } set { Interop.Elementary.elm_image_no_scale_set(RealHandle, !value); } } /// /// Sets or gets whether the object is down resizable. /// /// preview public bool CanScaleDown { get { return _canScaleDown; } set { _canScaleDown = value; Interop.Elementary.elm_image_resizable_set(RealHandle, _canScaleUp, _canScaleDown); } } /// /// Sets or gets whether the object is up resizable. /// /// preview public bool CanScaleUp { get { return _canScaleUp; } set { _canScaleUp = value; Interop.Elementary.elm_image_resizable_set(RealHandle, _canScaleUp, _canScaleDown); } } /// /// Sets or gets whether the image fills the entire object area, when keeping the aspect ratio. /// /// preview public bool CanFillOutside { get { return Interop.Elementary.elm_image_fill_outside_get(RealHandle); } set { Interop.Elementary.elm_image_fill_outside_set(RealHandle, value); } } /// /// Sets or gets the prescale size for the image. /// /// preview public int PrescaleSize { get { return Interop.Elementary.elm_image_prescale_get(RealHandle); } set { Interop.Elementary.elm_image_prescale_set(RealHandle, value); } } /// /// Sets or gets whether the original aspect ratio of the image should be kept on resize. /// /// preview public bool IsFixedAspect { get { return Interop.Elementary.elm_image_aspect_fixed_get(RealHandle); } set { Interop.Elementary.elm_image_aspect_fixed_set(RealHandle, value); } } /// /// Sets or gets whether an image object (which supports animation) is to animate itself. /// /// preview public bool IsAnimated { get { return Interop.Elementary.elm_image_animated_get(RealHandle); } set { Interop.Elementary.elm_image_animated_set(RealHandle, value); } } /// /// Gets whether an image object supports animation. /// /// preview public bool IsAnimatedAvailable { get { return Interop.Elementary.elm_image_animated_available_get(RealHandle); } } /// /// Sets or gets whether an image object is under animation. /// /// /// An image object, even if it supports animation, will be displayed by default without animation. /// To actually start playing any image object's animation, should be TRUE before setting this property true. /// /// preview public bool IsAnimationPlaying { get { return Interop.Elementary.elm_image_animated_play_get(RealHandle); } set { Interop.Elementary.elm_image_animated_play_set(RealHandle, value); } } /// /// Sets or gets whether the image is 'editable'. /// /// preview public bool IsEditable { get { return Interop.Elementary.elm_image_editable_get(RealHandle); } set { Interop.Elementary.elm_image_editable_set(RealHandle, value); } } /// /// Gets the current size of the image. /// /// preview public Size ObjectSize { get { Interop.Elementary.elm_image_object_size_get(RealHandle, out int w, out int h); return new Size(w, h); } } /// /// Sets or gets whether the alpha channel data is being used on the given image object. /// /// preview public bool IsOpaque { get { if (ImageObject != null) { return ImageObject.IsOpaque; } return false; } set { if (ImageObject != null) { ImageObject.IsOpaque = value; } } } /// /// Sets or gets the image orientation. /// /// preview public ImageOrientation Orientation { get { return (ImageOrientation)Interop.Elementary.elm_image_orient_get(RealHandle); } set { Interop.Elementary.elm_image_orient_set(RealHandle, (int)value); } } /// /// Sets or gets the image color. /// /// preview public override Color Color { get { return _color; } set { if (ImageObject != null) { if (value.IsDefault) { ImageObject.Color = Color.FromRgba(255, 255, 255, 255); } else { ImageObject.Color = value; } } _color = value; } } /// /// Sets the background color. /// /// preview public override Color BackgroundColor { set { if (value.IsDefault) { SetPartColor("bg", Color.Transparent); } else { SetPartColor("bg", value); } _backgroundColor = value; } } /// /// Gets the inlined image object of the image widget. /// This property allows one to get the underlying EvasObject of type Image from this elementary widget. It can be useful to do things like save the image to a file, etc. /// /// Be careful not to manipulate it, as it is under the control of the widget. /// preview public EvasImage ImageObject { get { if (_imageObject == null) { IntPtr evasObj = Interop.Elementary.elm_image_object_get(RealHandle); if (evasObj != IntPtr.Zero) { _imageObject = new EvasImage(this, evasObj); _imageObject.Deleted += (s, e) => _imageObject = null; } } return _imageObject; } } /// /// Sets the dimensions for an image object's border, a region which is not scaled together with its center ever. /// /// The border's left width. /// The border's right width. /// The border's top width. /// The border's bottom width. /// preview public void SetBorder(int left, int right, int top, int bottom) { ImageObject?.SetBorder(left, right, top, bottom); } /// /// Sets or gets if the center part of the given image object (not the border) should be drawn. /// /// /// When rendering, the image may be scaled to fit the size of the image object. /// This function sets if the center part of the scaled image is to be drawn or left completely blank, or forced to be solid. /// Very useful for frames and decorations. /// /// preview public ImageBorderFillMode BorderCenterFillMode { get { if (ImageObject != null) { return ImageObject.BorderCenterFillMode; } else { return default(ImageBorderFillMode); } } set { if (ImageObject != null) { ImageObject.BorderCenterFillMode = value; } } } /// /// Sets the file that is used as the image's source. /// /// The path to the file that is used as an image source. /// (true = success, false = error) /// preview public bool Load(string file) { if (file == null) throw new ArgumentNullException(nameof(file)); Interop.Elementary.elm_image_async_open_set(RealHandle, false); Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true); return Interop.Elementary.elm_image_file_set(RealHandle, file, null); } /// /// Sets the URI that is used as the image's source. /// /// The URI to the file that is used as an image source. /// (true = success, false = error) /// preview public bool Load(Uri uri) { if (uri == null) throw new ArgumentNullException(nameof(uri)); return Load(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri); } /// /// Sets a location in the memory to be used as an image object's source bitmap. /// /// /// This function is handy when the contents of an image file are mapped into the memory, for example, /// the format string should be something like "png", "jpg", "tga", "tiff", "bmp" etc, when provided (null, on the contrary). /// This improves the loader performance as it tries the "correct" loader first, before trying a range of other possible loaders until one succeeds. /// /// The binary data that is used as an image source. /// The size of the binary data blob img. /// (true = success, false = error) /// preview [Obsolete("This method will be removed. Use Load(Stream stream) instead.")] public unsafe bool Load(byte* img, long size) { if (img == null) throw new ArgumentNullException(nameof(img)); Interop.Elementary.elm_image_async_open_set(RealHandle, false); Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true); return Interop.Elementary.elm_image_memfile_set(RealHandle, img, size, IntPtr.Zero, IntPtr.Zero); } /// /// Sets the stream that is used as the image's source. /// /// The stream that is used as an image source. /// (true = success, false = error) /// preview public bool Load(Stream stream) { if (stream == null) throw new ArgumentNullException(nameof(stream)); Interop.Elementary.elm_image_async_open_set(RealHandle, false); Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true); MemoryStream memstream = new MemoryStream(); stream.CopyTo(memstream); unsafe { byte[] dataArr = memstream.ToArray(); fixed (byte* data = &dataArr[0]) { return Interop.Elementary.elm_image_memfile_set(RealHandle, data, dataArr.Length, IntPtr.Zero, IntPtr.Zero); } } } /// /// Sets the file that is used as the image's source with async. /// /// The path to the file that is used as an image source. /// The cancellation token. /// (true = success, false = error) /// preview public async Task LoadAsync(string file, CancellationToken cancellationToken = default(CancellationToken)) { if (file == null) throw new ArgumentNullException(nameof(file)); Interop.Elementary.elm_image_async_open_set(RealHandle, true); Interop.Elementary.elm_image_preload_disabled_set(RealHandle, false); var tcs = new TaskCompletionSource(); cancellationToken.Register(() => { if (tcs != null && !tcs.Task.IsCompleted) { tcs.SetCanceled(); } }); SmartEvent loadReady = new SmartEvent(this, RealHandle, "load,ready"); loadReady.On += (s, e) => { LoadingCompleted?.Invoke(this, EventArgs.Empty); if (tcs != null && !tcs.Task.IsCompleted) { tcs.SetResult(true); } }; SmartEvent loadError = new SmartEvent(this, RealHandle, "load,error"); loadError.On += (s, e) => { LoadingFailed?.Invoke(this, EventArgs.Empty); if (tcs != null && !tcs.Task.IsCompleted) { tcs.SetResult(false); } }; using (loadReady) using (loadError) { bool ret = Interop.Elementary.elm_image_file_set(RealHandle, file, null); if (!ret) { throw new InvalidOperationException("Failed to set file to Image"); } // it should be return on main thread, because SmartEvent should be disposed on main thread return await tcs.Task.ConfigureAwait(true); } } /// /// Sets the URI that is used as the image's source with async. /// /// The URI to the file that is used as an image source. /// The cancellation token. /// (true = success, false = error) /// preview public Task LoadAsync(Uri uri, CancellationToken cancellationToken = default(CancellationToken)) { if (uri == null) throw new ArgumentNullException(nameof(uri)); return LoadAsync(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri, cancellationToken); } /// /// Sets the stream that is used as the image's source with async. /// /// The stream that is used as an image source. /// The cancellation token. /// (true = success, false = error) /// preview public async Task LoadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { if (stream == null) throw new ArgumentNullException(nameof(stream)); Interop.Elementary.elm_image_async_open_set(RealHandle, true); Interop.Elementary.elm_image_preload_disabled_set(RealHandle, false); var tcs = new TaskCompletionSource(); cancellationToken.Register(() => { if (tcs != null && !tcs.Task.IsCompleted) { tcs.SetCanceled(); } }); SmartEvent loadReady = new SmartEvent(this, RealHandle, "load,ready"); loadReady.On += (s, e) => { LoadingCompleted?.Invoke(this, EventArgs.Empty); if (tcs != null && !tcs.Task.IsCompleted) { tcs.SetResult(true); } }; SmartEvent loadError = new SmartEvent(this, RealHandle, "load,error"); loadError.On += (s, e) => { LoadingFailed?.Invoke(this, EventArgs.Empty); if (tcs != null && !tcs.Task.IsCompleted) { tcs.SetResult(false); } }; using (MemoryStream memstream = new MemoryStream()) using (loadReady) using (loadError) { await stream.CopyToAsync(memstream).ConfigureAwait(true); unsafe { byte[] dataArr = memstream.ToArray(); fixed (byte* data = &dataArr[0]) { bool ret = Interop.Elementary.elm_image_memfile_set(RealHandle, data, dataArr.Length, IntPtr.Zero, IntPtr.Zero); if (!ret) { return false; } } } // it should be return on main thread, because SmartEvent should be disposed on main thread return await tcs.Task.ConfigureAwait(true); } } /// /// Sets the color of the Color class for a given widget. /// /// The name of the Color class. /// The struct of the Color class. /// preview public override void SetPartColor(string part, Color color) { Interop.Elementary.elm_object_color_class_color_set(Handle, part, color.R * color.A / 255, color.G * color.A / 255, color.B * color.A / 255, color.A); } /// /// Gets the color of the Color class for a given widget. /// /// The name of the Color class. /// The color object. /// preview public override Color GetPartColor(string part) { Interop.Elementary.elm_object_color_class_color_get(Handle, part, out int r, out int g, out int b, out int a); return new Color((int)(r / (a / 255.0)), (int)(g / (a / 255.0)), (int)(b / (a / 255.0)), a); } /// /// Sets the content at a part of a given container widget. /// /// The parent is a given container, which will be attached by the image as a child. It's type. /// The new object, otherwise null if it cannot be created. /// preview protected override IntPtr CreateHandle(EvasObject parent) { IntPtr handle = Interop.Elementary.elm_layout_add(parent.Handle); Interop.Elementary.elm_layout_theme_set(handle, "layout", "background", "default"); RealHandle = Interop.Elementary.elm_image_add(handle); Interop.Elementary.elm_object_part_content_set(handle, "elm.swallow.content", RealHandle); return handle; } } /// /// Enumeration for the fill mode of the image border. /// /// preview public enum ImageBorderFillMode { /// /// None mode of the image border. /// None, /// /// Default mode of the image border. /// Default, /// /// Solid mode of the image border. /// Solid, } /// /// Enumeration for the possible orientation options. /// /// preview public enum ImageOrientation : int { /// /// No orientation change. /// None = 0, /// /// Rotate 90 degrees clockwise. /// Rotate90, /// /// Rotate 180 degrees clockwise. /// Rotate180, /// /// Rotate 90 degrees counter-clockwise (i.e., 270 degrees clockwise). /// Rotate270, /// /// Flip the image horizontally. /// FlipHorizontal, /// /// Flip the image vertically. /// FlipVertical, /// /// Flip the image along the Y = (width - X) line (bottom-left to top-right). /// FlipTranspose, /// /// Flip the image along the Y = X line (top-left to bottom-right). /// FlipTransverse } }