Merge remote-tracking branch 'uix-stt-engine/tizen'
[platform/core/csapi/tizenfx.git] / src / ElmSharp / ElmSharp / Image.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 using System;
18 using System.IO;
19 using System.Threading;
20 using System.Threading.Tasks;
21
22 namespace ElmSharp
23 {
24     /// <summary>
25     /// The Image is a widget that allows one to load and display an image file on it,
26     /// be it from a disk file or from a memory region.
27     /// Inherits Widget
28     /// </summary>
29     public class Image : Widget
30     {
31         bool _canScaleUp = true;
32         bool _canScaleDown = true;
33         SmartEvent _clicked;
34         Color _color = Color.Default;
35
36         /// <summary>
37         /// Creates and initializes a new instance of Image class.
38         /// </summary>
39         /// <param name="parent">The parent is a given container which will be attached by Image as a child. It's <see cref="EvasObject"/> type.</param>
40         public Image(EvasObject parent) : base(parent)
41         {
42             _clicked = new SmartEvent(this, "clicked");
43             _clicked.On += (s, e) => Clicked?.Invoke(this, EventArgs.Empty);
44         }
45
46         /// <summary>
47         /// Clicked will be triggered when the image is clicked.
48         /// </summary>
49         public event EventHandler Clicked;
50
51         /// <summary>
52         /// LoadingCompleted will be triggered when the image is loaded completely.
53         /// </summary>
54         public event EventHandler LoadingCompleted;
55
56         /// <summary>
57         /// Clicked will be triggered when the image is fail to load.
58         /// </summary>
59         public event EventHandler LoadingFailed;
60
61         /// <summary>
62         /// Gets the file that is used as an image.
63         /// </summary>
64         public string File
65         {
66             get
67             {
68                 return Interop.Elementary.elm_image_file_get(RealHandle);
69             }
70         }
71
72         /// <summary>
73         /// Sets or gets the smooth effect for an image.
74         /// </summary>
75         public bool IsSmooth
76         {
77             get
78             {
79                 return Interop.Elementary.elm_image_smooth_get(RealHandle);
80             }
81             set
82             {
83                 Interop.Elementary.elm_image_smooth_set(RealHandle, value);
84             }
85         }
86
87         /// <summary>
88         /// Sets or gets whether scaling is disabled on the object.
89         /// </summary>
90         public bool IsScaling
91         {
92             get
93             {
94                 return !Interop.Elementary.elm_image_no_scale_get(RealHandle);
95             }
96             set
97             {
98                 Interop.Elementary.elm_image_no_scale_set(RealHandle, !value);
99             }
100         }
101
102         /// <summary>
103         /// Sets or gets whether the object is down resizeable.
104         /// </summary>
105         public bool CanScaleDown
106         {
107             get
108             {
109                 return _canScaleDown;
110             }
111             set
112             {
113                 _canScaleDown = value;
114                 Interop.Elementary.elm_image_resizable_set(RealHandle, _canScaleUp, _canScaleDown);
115             }
116         }
117
118         /// <summary>
119         /// Sets or gets whether the object is up resizeable.
120         /// </summary>
121         public bool CanScaleUp
122         {
123             get
124             {
125                 return _canScaleUp;
126             }
127             set
128             {
129                 _canScaleUp = value;
130                 Interop.Elementary.elm_image_resizable_set(RealHandle, _canScaleUp, _canScaleDown);
131             }
132         }
133
134         /// <summary>
135         /// Sets or gets whether the image fills the entire object area, when keeping the aspect ratio.
136         /// </summary>
137         public bool CanFillOutside
138         {
139             get
140             {
141                 return Interop.Elementary.elm_image_fill_outside_get(RealHandle);
142             }
143             set
144             {
145                 Interop.Elementary.elm_image_fill_outside_set(RealHandle, value);
146             }
147         }
148
149         /// <summary>
150         /// Sets or gets the prescale size for the image.
151         /// </summary>
152         public int PrescaleSize
153         {
154             get
155             {
156                 return Interop.Elementary.elm_image_prescale_get(RealHandle);
157             }
158             set
159             {
160                 Interop.Elementary.elm_image_prescale_set(RealHandle, value);
161             }
162         }
163
164         /// <summary>
165         /// Sets or gets whether the original aspect ratio of the image should be kept on resize.
166         /// </summary>
167         public bool IsFixedAspect
168         {
169             get
170             {
171                 return Interop.Elementary.elm_image_aspect_fixed_get(RealHandle);
172             }
173             set
174             {
175                 Interop.Elementary.elm_image_aspect_fixed_set(RealHandle, value);
176             }
177         }
178
179         /// <summary>
180         /// Sets or gets whether an image object (which supports animation) is to animate itself.
181         /// </summary>
182         public bool IsAnimated
183         {
184             get
185             {
186                 return Interop.Elementary.elm_image_animated_get(RealHandle);
187             }
188             set
189             {
190                 Interop.Elementary.elm_image_animated_set(RealHandle, value);
191             }
192         }
193
194         /// <summary>
195         /// Gets whether an image object supports animation.
196         /// </summary>
197         public bool IsAnimatedAvailable
198         {
199             get
200             {
201                 return Interop.Elementary.elm_image_animated_available_get(RealHandle);
202             }
203         }
204
205         /// <summary>
206         /// Sets or gets whether an image object is under animation.
207         /// </summary>
208         /// <remarks>
209         /// An image object, even if it supports animation, will be displayed by default without animation.
210         /// To actually start playing any image object's animation, <see cref="IsAnimated"/> should be TRUE before setting this property true.
211         /// </remarks>
212         public bool IsAnimationPlaying
213         {
214             get
215             {
216                 return Interop.Elementary.elm_image_animated_play_get(RealHandle);
217             }
218             set
219             {
220                 Interop.Elementary.elm_image_animated_play_set(RealHandle, value);
221             }
222         }
223
224         /// <summary>
225         /// Sets or gets whether the image is 'editable'.
226         /// </summary>
227         public bool IsEditable
228         {
229             get
230             {
231                 return Interop.Elementary.elm_image_editable_get(RealHandle);
232             }
233             set
234             {
235                 Interop.Elementary.elm_image_editable_set(RealHandle, value);
236             }
237         }
238
239         /// <summary>
240         /// Gets the current size of the image.
241         /// </summary>
242         public Size ObjectSize
243         {
244             get
245             {
246                 Interop.Elementary.elm_image_object_size_get(RealHandle, out int w, out int h);
247                 return new Size(w, h);
248             }
249         }
250
251         /// <summary>
252         /// Sets or gets whether alpha channel data is being used on the given image object.
253         /// </summary>
254         public bool IsOpaque
255         {
256             get
257             {
258                 IntPtr evasObj = Interop.Elementary.elm_image_object_get(RealHandle);
259                 if (evasObj != IntPtr.Zero)
260                 {
261                     return !Interop.Evas.evas_object_image_alpha_get(evasObj);
262                 }
263                 return false;
264             }
265             set
266             {
267                 IntPtr evasObj = Interop.Elementary.elm_image_object_get(RealHandle);
268                 if (evasObj != IntPtr.Zero)
269                 {
270                     Interop.Evas.evas_object_image_alpha_set(evasObj, !value);
271                 }
272             }
273         }
274
275         /// <summary>
276         /// Sets or gets the image orientation.
277         /// </summary>
278         public ImageOrientation Orientation
279         {
280             get
281             {
282                 return (ImageOrientation)Interop.Elementary.elm_image_orient_get(RealHandle);
283             }
284             set
285             {
286                 Interop.Elementary.elm_image_orient_set(RealHandle, (int)value);
287             }
288         }
289
290         /// <summary>
291         /// Sets or gets the image color
292         /// </summary>
293         public override Color Color
294         {
295             get
296             {
297                 return _color;
298             }
299             set
300             {
301                 IntPtr evasObj = Interop.Elementary.elm_image_object_get(RealHandle);
302                 if (evasObj != IntPtr.Zero)
303                 {
304                     if (value.IsDefault)
305                     {
306                         // Currently, we assume the Image.Color property as a blending color (actually, multiply blending).
307                         // Thus we are using Color.White (255,255,255,255) as a default color to ensure original image color. (255/255 * original = original)
308                         Interop.Evas.evas_object_color_set(evasObj, 255, 255, 255, 255);
309                     }
310                     else
311                     {
312                         Interop.Evas.SetPremultipliedColor(evasObj, value.R, value.G, value.B, value.A);
313                     }
314                 }
315                 _color = value;
316             }
317         }
318
319         /// <summary>
320         /// Sets the background color
321         /// </summary>
322         public override Color BackgroundColor
323         {
324             set
325             {
326                 if (value.IsDefault)
327                 {
328                     SetPartColor("bg", Color.Transparent);
329                 }
330                 else
331                 {
332                     SetPartColor("bg", value);
333                 }
334                 _backgroundColor = value;
335             }
336         }
337
338         /// <summary>
339         /// Sets the dimensions for an image object's border, a region which is not scaled together with its center ever.
340         /// </summary>
341         /// <param name="left">The border's left width</param>
342         /// <param name="right">The border's right width</param>
343         /// <param name="top">The border's top width</param>
344         /// <param name="bottom">The border's bottom width</param>
345         public void SetBorder(int left, int right, int top, int bottom)
346         {
347             IntPtr evasObj = Interop.Elementary.elm_image_object_get(RealHandle);
348             Interop.Evas.evas_object_image_border_set(evasObj, left, right, top, bottom);
349         }
350
351         /// <summary>
352         /// Sets or gets if the center part of the given image object (not the border) should be drawn.
353         /// </summary>
354         /// <remarks>
355         /// When rendering, the image may be scaled to fit the size of the image object.
356         /// This function sets if the center part of the scaled image is to be drawn or left completely blank, or forced to be solid.
357         /// Very useful for frames and decorations.
358         /// </remarks>
359         public ImageBorderFillMode BorderCenterFillMode
360         {
361             get
362             {
363                 IntPtr evasObj = Interop.Elementary.elm_image_object_get(RealHandle);
364                 return (ImageBorderFillMode)Interop.Evas.evas_object_image_border_center_fill_get(evasObj);
365             }
366             set
367             {
368                 IntPtr evasObj = Interop.Elementary.elm_image_object_get(RealHandle);
369                 Interop.Evas.evas_object_image_border_center_fill_set(evasObj, (int)value);
370             }
371         }
372
373         /// <summary>
374         /// Sets the file that is used as the image's source.
375         /// </summary>
376         /// <param name="file">The path to the file that is used as an image source</param>
377         /// <returns>(true = success, false = error)</returns>
378         public bool Load(string file)
379         {
380             if (file == null)
381                 throw new ArgumentNullException("file");
382
383             Interop.Elementary.elm_image_async_open_set(RealHandle, false);
384             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true);
385             return Interop.Elementary.elm_image_file_set(RealHandle, file, null);
386         }
387
388         /// <summary>
389         /// Sets the uri that is used as the image's source.
390         /// </summary>
391         /// <param name="uri">The uri to the file that is used as an image source</param>
392         /// <returns>(true = success, false = error)</returns>
393         public bool Load(Uri uri)
394         {
395             if (uri == null)
396                 throw new ArgumentNullException("uri");
397
398             return Load(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri);
399         }
400
401         /// <summary>
402         /// Sets a location in the memory to be used as an image object's source bitmap.
403         /// </summary>
404         /// <remarks>
405         /// This function is handy when the contents of an image file are mapped into the memory, for example.
406         /// The format string should be something like "png", "jpg", "tga", "tiff", "bmp" etc, when provided (null, on the contrary).
407         /// This improves the loader performance as it tries the "correct" loader first, before trying a range of other possible loaders until one succeeds.
408         /// </remarks>
409         /// <param name="img">The binary data that is used as an image source</param>
410         /// <param name="size">The size of the binary data blob img</param>
411         /// <returns>(true = success, false = error)</returns>
412         [Obsolete("This method will be removed. Use Load(Stream stream) instead.")]
413         public unsafe bool Load(byte* img, long size)
414         {
415             if (img == null)
416                 throw new ArgumentNullException("img");
417
418             Interop.Elementary.elm_image_async_open_set(RealHandle, false);
419             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true);
420             return Interop.Elementary.elm_image_memfile_set(RealHandle, img, size, IntPtr.Zero, IntPtr.Zero);
421         }
422
423         /// <summary>
424         /// Sets the stream that is used as the image's source.
425         /// </summary>
426         /// <param name="stream">The stream that is used as an image source</param>
427         /// <returns>(true = success, false = error)</returns>
428         public bool Load(Stream stream)
429         {
430             if (stream == null)
431                 throw new ArgumentNullException("stream");
432
433             Interop.Elementary.elm_image_async_open_set(RealHandle, false);
434             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true);
435             MemoryStream memstream = new MemoryStream();
436             stream.CopyTo(memstream);
437             unsafe
438             {
439                 byte[] dataArr = memstream.ToArray();
440                 fixed (byte* data = &dataArr[0])
441                 {
442                     return Interop.Elementary.elm_image_memfile_set(RealHandle, data, dataArr.Length, IntPtr.Zero, IntPtr.Zero);
443                 }
444             }
445         }
446
447         /// <summary>
448         /// Sets the file that is used as the image's source with async.
449         /// </summary>
450         /// <param name="file">The path to the file that is used as an image source</param>
451         /// <param name="cancellationToken">cancellation token</param>
452         /// <returns>(true = success, false = error)</returns>
453         public Task<bool> LoadAsync(string file, CancellationToken cancellationToken = default(CancellationToken))
454         {
455             if (file == null)
456                 throw new ArgumentNullException("file");
457
458             Interop.Elementary.elm_image_async_open_set(RealHandle, true);
459             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, false);
460
461             var tcs = new TaskCompletionSource<bool>();
462
463             cancellationToken.Register(() =>
464             {
465                 if (tcs != null && !tcs.Task.IsCompleted)
466                 {
467                     tcs.SetCanceled();
468                 }
469             });
470
471             SmartEvent loadReady = new SmartEvent(this, RealHandle, "load,ready");
472             loadReady.On += (s, e) =>
473             {
474                 loadReady.Dispose();
475                 LoadingCompleted?.Invoke(this, EventArgs.Empty);
476                 if (tcs != null && !tcs.Task.IsCompleted)
477                 {
478                     tcs.SetResult(true);
479                 }
480             };
481
482             SmartEvent loadError = new SmartEvent(this, RealHandle, "load,error");
483             loadError.On += (s, e) =>
484             {
485                 loadError.Dispose();
486                 LoadingFailed?.Invoke(this, EventArgs.Empty);
487                 if (tcs != null && !tcs.Task.IsCompleted)
488                 {
489                     tcs.SetResult(false);
490                 }
491             };
492
493             bool ret = Interop.Elementary.elm_image_file_set(RealHandle, file, null);
494             if (!ret)
495             {
496                 throw new InvalidOperationException("Failed to set file to Image");
497             }
498
499             return tcs.Task;
500         }
501
502         /// <summary>
503         /// Sets the uri that is used as the image's source with async.
504         /// </summary>
505         /// <param name="uri">The uri to the file that is used as an image source</param>
506         /// <param name="cancellationToken">cancellation token</param>
507         /// <returns>(true = success, false = error)</returns>
508         public Task<bool> LoadAsync(Uri uri, CancellationToken cancellationToken = default(CancellationToken))
509         {
510             if (uri == null)
511                 throw new ArgumentNullException("uri");
512
513             return LoadAsync(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri, cancellationToken);
514         }
515
516         /// <summary>
517         /// Sets the stream that is used as the image's source with async.
518         /// </summary>
519         /// <param name="stream">The stream that is used as an image source</param>
520         /// <param name="cancellationToken">cancellation token</param>
521         /// <returns>(true = success, false = error)</returns>
522         public async Task<bool> LoadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
523         {
524             if (stream == null)
525                 throw new ArgumentNullException("stream");
526
527             Interop.Elementary.elm_image_async_open_set(RealHandle, true);
528             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, false);
529
530             var tcs = new TaskCompletionSource<bool>();
531
532             cancellationToken.Register(() =>
533             {
534                 if (tcs != null && !tcs.Task.IsCompleted)
535                 {
536                     tcs.SetCanceled();
537                 }
538             });
539
540             SmartEvent loadReady = new SmartEvent(this, RealHandle, "load,ready");
541             loadReady.On += (s, e) =>
542             {
543                 loadReady.Dispose();
544                 LoadingCompleted?.Invoke(this, EventArgs.Empty);
545                 if (tcs != null && !tcs.Task.IsCompleted)
546                 {
547                     tcs.SetResult(true);
548                 }
549             };
550
551             SmartEvent loadError = new SmartEvent(this, RealHandle, "load,error");
552             loadError.On += (s, e) =>
553             {
554                 loadError.Dispose();
555                 LoadingFailed?.Invoke(this, EventArgs.Empty);
556                 if (tcs != null && !tcs.Task.IsCompleted)
557                 {
558                     tcs.SetResult(false);
559                 }
560             };
561
562             MemoryStream memstream = new MemoryStream();
563             await stream.CopyToAsync(memstream);
564
565             unsafe
566             {
567                 byte[] dataArr = memstream.ToArray();
568                 fixed (byte* data = &dataArr[0])
569                 {
570                     bool ret = Interop.Elementary.elm_image_memfile_set(RealHandle, data, dataArr.Length, IntPtr.Zero, IntPtr.Zero);
571                     if (!ret)
572                     {
573                         return false;
574                     }
575                 }
576             }
577
578             return await tcs.Task;
579         }
580
581         /// <summary>
582         /// Sets the color of color class for a given widget.
583         /// </summary>
584         /// <param name="part">The name of color class.</param>
585         /// <param name="color">The struct of color</param>
586         public override void SetPartColor(string part, Color color)
587         {
588             Interop.Elementary.elm_object_color_class_color_set(Handle, part, color.R * color.A / 255,
589                                                                               color.G * color.A / 255,
590                                                                               color.B * color.A / 255,
591                                                                               color.A);
592         }
593
594         /// <summary>
595         /// Gets the color of color class for a given widget.
596         /// </summary>
597         /// <param name="part">The name of color class.</param>
598         /// <returns>color object</returns>
599         public override Color GetPartColor(string part)
600         {
601             Interop.Elementary.elm_object_color_class_color_get(Handle, part, out int r, out int g, out int b, out int a);
602             return new Color((int)(r / (a / 255.0)), (int)(g / (a / 255.0)), (int)(b / (a / 255.0)), a);
603         }
604
605         /// <summary>
606         /// Sets the content at a part of a given container widget.
607         /// </summary>
608         /// <param name="parent">The parent is a given container which will be attached by Image as a child. It's <see cref="EvasObject"/> type.</param>
609         /// <returns>The new object, otherwise null if it cannot be created</returns>
610         protected override IntPtr CreateHandle(EvasObject parent)
611         {
612             IntPtr handle = Interop.Elementary.elm_layout_add(parent);
613             Interop.Elementary.elm_layout_theme_set(handle, "layout", "background", "default");
614
615             RealHandle = Interop.Elementary.elm_image_add(handle);
616             Interop.Elementary.elm_object_part_content_set(handle, "elm.swallow.content", RealHandle);
617
618             return handle;
619         }
620     }
621
622     /// <summary>
623     /// Enumeration for the fill mode of image border
624     /// </summary>
625     public enum ImageBorderFillMode
626     {
627         /// <summary>
628         /// None mode of image border
629         /// </summary>
630         None,
631
632         /// <summary>
633         /// Default mode of image border
634         /// </summary>
635         Default,
636
637         /// <summary>
638         /// Solid mode of image border
639         /// </summary>
640         Solid,
641     }
642
643     /// <summary>
644     /// Enumeration for the possible orientation options
645     /// </summary>
646     public enum ImageOrientation : int
647     {
648         /// <summary>
649         /// No orientation change
650         /// </summary>
651         None = 0,
652
653         /// <summary>
654         /// Rotate 90 degrees clockwise
655         /// </summary>
656         Rotate90,
657
658         /// <summary>
659         /// Rotate 180 degrees clockwise
660         /// </summary>
661         Rotate180,
662
663         /// <summary>
664         /// Rotate 90 degrees counter-clockwise (i.e. 270 degrees clockwise)
665         /// </summary>
666         Rotate270,
667
668         /// <summary>
669         /// Flip image horizontally
670         /// </summary>
671         FlipHorizontal,
672
673         /// <summary>
674         /// Flip image vertically
675         /// </summary>
676         FlipVertical,
677
678         /// <summary>
679         /// Flip the image along the y = (width - x) line (bottom-left to top-right)
680         /// </summary>
681         FlipTranspose,
682
683         /// <summary>
684         /// Flip the image along the y = x line (top-left to bottom-right)
685         /// </summary>
686         FlipTransverse
687     }
688 }