X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2FTizen.NUI%2Fsrc%2Fpublic%2FBaseComponents%2FImageView.cs;h=af61687e383bb04933aa8a3a5bbfe799a9ff016f;hb=372c241de854d7b3da99a15c1167ef2912c4568d;hp=e22ffefa0faa236b268d6dfb5bca764f41cfd5b1;hpb=e911f8514b75819ef210ec0d66f7096e66b87062;p=platform%2Fcore%2Fcsapi%2Ftizenfx.git diff --git a/src/Tizen.NUI/src/public/BaseComponents/ImageView.cs b/src/Tizen.NUI/src/public/BaseComponents/ImageView.cs index e22ffef..af61687 100755 --- a/src/Tizen.NUI/src/public/BaseComponents/ImageView.cs +++ b/src/Tizen.NUI/src/public/BaseComponents/ImageView.cs @@ -15,6 +15,7 @@ * */ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.ComponentModel; using Tizen.NUI.Binding; @@ -36,16 +37,55 @@ namespace Tizen.NUI.BaseComponents private EventHandler _resourceLoadedEventHandler; private _resourceLoadedCallbackType _resourceLoadedCallback; + /// + /// Convert non-null string that some keyword change as application specific directory. + /// + /// Inputed and replaced after this function finished + /// Replaced url + private static string ConvertResourceUrl(ref string value) + { + value ??= ""; + if (value.StartsWith("*Resource*")) + { + string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource; + value = value.Replace("*Resource*", resource); + } + return value; + } + + // Collection of image-sensitive properties. + private static readonly List cachedImagePropertyKeyList = new List { + Visual.Property.Type, + ImageVisualProperty.URL, + ImageVisualProperty.AlphaMaskURL, + ImageVisualProperty.CropToMask, + Visual.Property.VisualFittingMode, + ImageVisualProperty.DesiredWidth, + ImageVisualProperty.DesiredHeight, + ImageVisualProperty.ReleasePolicy, + ImageVisualProperty.WrapModeU, + ImageVisualProperty.WrapModeV, + ImageVisualProperty.SynchronousLoading, + Visual.Property.PremultipliedAlpha, + ImageVisualProperty.OrientationCorrection, + ImageVisualProperty.FastTrackUploading, + NpatchImageVisualProperty.Border, + NpatchImageVisualProperty.BorderOnly, + }; + internal PropertyMap cachedImagePropertyMap; + internal bool imagePropertyUpdatedFlag = false; + + private bool imagePropertyUpdateProcessAttachedFlag = false; private Rectangle _border; private string _resourceUrl = ""; - private bool _synchronousLoading = false; - private string _alphaMaskUrl = null; private int _desired_width = -1; private int _desired_height = -1; - private VisualFittingModeType _fittingMode = VisualFittingModeType.Fill; + private bool _fastTrackUploading = false; private TriggerableSelector resourceUrlSelector; private TriggerableSelector borderSelector; + private RelativeVector4 internalPixelArea; + /// /// Creates an initialized ImageView. /// @@ -79,9 +119,15 @@ namespace Tizen.NUI.BaseComponents /// /// The URL of the image resource to display. /// 3 - public ImageView(string url) : this(Interop.ImageView.New(url), true) + public ImageView(string url) : this(Interop.ImageView.New(ConvertResourceUrl(ref url)), true) { - ResourceUrl = url; + _resourceUrl = url; + + // Update cached property. Note that we should not re-create new visual. + using (PropertyValue urlValue = new PropertyValue(_resourceUrl)) + { + UpdateImage(ImageVisualProperty.URL, urlValue, false); + } if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); } @@ -93,16 +139,38 @@ namespace Tizen.NUI.BaseComponents /// false : Not displayed (hidden), true : displayed (shown) /// This will be public opened in next release of tizen after ACR done. Before ACR, it is used as HiddenAPI (InhouseAPI). [EditorBrowsable(EditorBrowsableState.Never)] - public ImageView(string url, bool shown) : this(Interop.ImageView.New(url), true) + public ImageView(string url, bool shown) : this(Interop.ImageView.New(ConvertResourceUrl(ref url)), true) { - ResourceUrl = url; + _resourceUrl = url; + + // Update cached property. Note that we should not re-create new visual. + using (PropertyValue urlValue = new PropertyValue(_resourceUrl)) + { + UpdateImage(ImageVisualProperty.URL, urlValue, false); + } if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); SetVisible(shown); } - internal ImageView(string url, Uint16Pair size, bool shown = true) : this(Interop.ImageView.New(url, Uint16Pair.getCPtr(size)), true) + internal ImageView(string url, Uint16Pair size, bool shown = true) : this(Interop.ImageView.New(ConvertResourceUrl(ref url), Uint16Pair.getCPtr(size)), true) { - ResourceUrl = url; + _resourceUrl = url; + _desired_width = size?.GetWidth() ?? -1; + _desired_height = size?.GetHeight() ?? -1; + + // Update cached property. Note that we should not re-create new visual. + using (PropertyValue urlValue = new PropertyValue(_resourceUrl)) + { + UpdateImage(ImageVisualProperty.URL, urlValue, false); + } + using (PropertyValue desiredWidthValue = new PropertyValue(_desired_width)) + { + UpdateImage(ImageVisualProperty.DesiredWidth, desiredWidthValue, false); + } + using (PropertyValue desiredHeightValue = new PropertyValue(_desired_height)) + { + UpdateImage(ImageVisualProperty.DesiredWidth, desiredHeightValue, false); + } if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); if (!shown) @@ -216,6 +284,24 @@ namespace Tizen.NUI.BaseComponents } /// + /// Enumeration for MaskingMode of image. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum MaskingModeType + { + /// + /// Applies alpha masking on rendering time. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + MaskingOnRendering, + /// + /// Applies alpha masking on loading time. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + MaskingOnLoading + } + + /// /// ImageView ResourceUrl, type string. /// This is one of mandatory property. Even if not set or null set, it sets empty string ("") internally. /// When it is set as null, it gives empty string ("") to be read. @@ -235,11 +321,11 @@ namespace Tizen.NUI.BaseComponents } /// - /// This will be deprecated, please use Image instead.
+ /// This will be deprecated, Use Image instead.
/// ImageView ImageMap, type PropertyMap: string if it is a URL, map otherwise. ///
/// 3 - [Obsolete("Please do not use! This will be deprecated! Please use Image property instead!")] + [Obsolete("Do not use this, that will be deprecated. Use Image property instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public PropertyMap ImageMap { @@ -259,10 +345,20 @@ namespace Tizen.NUI.BaseComponents { if (_border == null) { + // Sync as current properties + UpdateImage(); + + // Get current properties force. PropertyMap returnValue = new PropertyMap(); PropertyValue image = GetProperty(ImageView.Property.IMAGE); image?.Get(returnValue); image?.Dispose(); + + // Update cached property map + if (returnValue != null) + { + MergeCachedImageVisualProperty(returnValue); + } return returnValue; } else @@ -276,6 +372,13 @@ namespace Tizen.NUI.BaseComponents { PropertyValue setValue = new Tizen.NUI.PropertyValue(value); SetProperty(ImageView.Property.IMAGE, setValue); + + // Image properties are changed hardly. We should ignore lazy UpdateImage + imagePropertyUpdatedFlag = false; + cachedImagePropertyMap?.Dispose(); + cachedImagePropertyMap = null; + MergeCachedImageVisualProperty(value); + NotifyPropertyChanged(); setValue?.Dispose(); } @@ -355,8 +458,7 @@ namespace Tizen.NUI.BaseComponents { get { - RelativeVector4 temp = (RelativeVector4)GetValue(PixelAreaProperty); - return new RelativeVector4(OnPixelAreaChanged, temp.X, temp.Y, temp.Z, temp.W); + return (RelativeVector4)GetValue(PixelAreaProperty); } set { @@ -420,7 +522,7 @@ namespace Tizen.NUI.BaseComponents /// Gets or sets whether to synchronous loading the resourceurl of image.
///
/// 3 - [Obsolete("Deprecated since API level 9 and will be removed in API level 11. Please use SynchronousLoading instead!")] + [Obsolete("This has been deprecated since API9 and will be removed in API11. Use SynchronousLoading instead.")] public bool SynchronosLoading { get @@ -438,7 +540,7 @@ namespace Tizen.NUI.BaseComponents /// /// /// Changing this property make this ImageView load image synchronously at the next loading - /// by following operation: , , + /// by following operation: , , /// and by some properties those cause reloading: , and etc. /// /// 9 @@ -473,6 +575,102 @@ namespace Tizen.NUI.BaseComponents } /// + /// Gets or sets whether to apply mask on GPU or not.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)] + public MaskingModeType MaskingMode + { + get + { + return (MaskingModeType)GetValue(MaskingModeProperty); + } + set + { + SetValue(MaskingModeProperty, value); + NotifyPropertyChanged(); + } + } + + private MaskingModeType InternalMaskingMode + { + get + { + int ret = (int)MaskingModeType.MaskingOnLoading; + + PropertyValue maskingMode = GetCachedImageVisualProperty(ImageVisualProperty.MaskingMode); + maskingMode?.Get(out ret); + maskingMode?.Dispose(); + + return (MaskingModeType)ret; + } + set + { + MaskingModeType ret = value; + PropertyValue setValue = new PropertyValue((int)ret); + UpdateImage(ImageVisualProperty.MaskingMode, setValue); + setValue?.Dispose(); + } + } + + /// + /// Gets or sets whether to apply fast track uploading or not.
+ ///
+ /// + /// If we use fast track uploading feature, It can upload texture without event-thead dependency. But also,
+ /// - Texture size is invalid until ResourceReady signal comes.
+ /// - Texture cannot be cached (We always try to load new image).
+ /// - Seamless visual change didn't supported.
+ /// - Alpha masking didn't supported. If you try, It will load as normal case.
+ /// - Synchronous loading didn't supported. If you try, It will load as normal case.
+ /// - Reload action didn't supported. If you try, It will load as normal case.
+ /// - Atlas loading didn't supported. If you try, It will load as normal case.
+ /// - Custom shader didn't supported. If you try, It will load as normal case. + ///
+ [EditorBrowsable(EditorBrowsableState.Never)] + public bool FastTrackUploading + { + get + { + return (bool)GetValue(FastTrackUploadingProperty); + } + set + { + SetValue(FastTrackUploadingProperty, value); + NotifyPropertyChanged(); + } + } + + private bool InternalFastTrackUploading + { + get + { + PropertyValue fastTrackUploading = GetCachedImageVisualProperty(ImageVisualProperty.FastTrackUploading); + fastTrackUploading?.Get(out _fastTrackUploading); + fastTrackUploading?.Dispose(); + + return _fastTrackUploading; + } + set + { + if (_fastTrackUploading != value) + { + _fastTrackUploading = value; + + PropertyValue setValue = new PropertyValue(_fastTrackUploading); + UpdateImage(ImageVisualProperty.FastTrackUploading, setValue); + setValue?.Dispose(); + + if (_fastTrackUploading && !string.IsNullOrEmpty(_resourceUrl)) + { + // Special case. If user set FastTrackUploading mean, user want to upload image As-Soon-As-Possible. + // Create ImageVisual synchronously. + UpdateImage(); + } + } + } + } + + /// /// Gets the loading state of the visual resource. /// /// 5 @@ -488,10 +686,9 @@ namespace Tizen.NUI.BaseComponents /// Downcasts a handle to imageView handle. /// /// Thrown when handle is null. - /// Please do not use! this will be deprecated! - /// Instead please use as keyword. + /// Do not use this, that will be deprecated. Use as keyword instead. /// 3 - [Obsolete("Please do not use! This will be deprecated! Please use as keyword instead! " + + [Obsolete("Do not use this, that will be deprecated. Use as keyword instead. " + "Like: " + "BaseHandle handle = new ImageView(imagePath); " + "ImageView image = handle as ImageView")] @@ -523,14 +720,20 @@ namespace Tizen.NUI.BaseComponents if (url.Contains(".json")) { - Tizen.Log.Fatal("NUI", "[ERROR] Please DO NOT set lottie file in ImageView! This is temporary checking, will be removed soon!"); + Tizen.Log.Fatal("NUI", "[ERROR] Do not set lottie file in ImageView! This is temporary checking, will be removed soon!"); return; } - Interop.ImageView.SetImage(SwigCPtr, url); + Interop.ImageView.SetImage(SwigCPtr, ConvertResourceUrl(ref url)); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); - ResourceUrl = url; + _resourceUrl = url; + // Update cached property. Note that we should not re-create new visual. + using (PropertyValue urlValue = new PropertyValue(_resourceUrl)) + { + UpdateImage(ImageVisualProperty.URL, urlValue, false); + } + imagePropertyUpdatedFlag = false; } /// @@ -553,9 +756,11 @@ namespace Tizen.NUI.BaseComponents /// 5 public void Reload() { - PropertyValue attributes = new PropertyValue(0); - this.DoAction(ImageView.Property.IMAGE, Property.ActionReload, attributes); - attributes?.Dispose(); + // Sync as current properties + UpdateImage(); + + + Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionReload); } /// @@ -564,9 +769,11 @@ namespace Tizen.NUI.BaseComponents /// 5 public void Play() { - PropertyValue attributes = new PropertyValue(0); - this.DoAction(ImageView.Property.IMAGE, Property.ActionPlay, attributes); - attributes?.Dispose(); + // Sync as current properties + UpdateImage(); + + + Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionPlay); } /// @@ -575,9 +782,11 @@ namespace Tizen.NUI.BaseComponents /// 5 public void Pause() { - PropertyValue attributes = new PropertyValue(0); - this.DoAction(ImageView.Property.IMAGE, Property.ActionPause, attributes); - attributes?.Dispose(); + // Sync as current properties + UpdateImage(); + + + Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionPause); } /// @@ -586,9 +795,12 @@ namespace Tizen.NUI.BaseComponents /// 5 public void Stop() { - PropertyValue attributes = new PropertyValue(0); - this.DoAction(ImageView.Property.IMAGE, Property.ActionStop, attributes); - attributes?.Dispose(); + // Sync as current properties + UpdateImage(); + + + + Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionStop); } /// @@ -615,30 +827,26 @@ namespace Tizen.NUI.BaseComponents get { string ret = ""; - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue maskUrl = imageMap?.Find(ImageVisualProperty.AlphaMaskURL); - maskUrl?.Get(out ret); - _alphaMaskUrl = ret; - imageMap?.Dispose(); - image?.Dispose(); + PropertyValue maskUrl = GetCachedImageVisualProperty(ImageVisualProperty.AlphaMaskURL); + maskUrl?.Get(out ret); maskUrl?.Dispose(); return ret; } set { - if (value == null) + PropertyValue setValue = new PropertyValue(value ?? ""); + UpdateImage(ImageVisualProperty.AlphaMaskURL, setValue); + // When we never set CropToMask property before, we should set default value as true. + using (PropertyValue cropToMask = GetCachedImageVisualProperty(ImageVisualProperty.CropToMask)) { - value = ""; + if (cropToMask == null) + { + using PropertyValue setCropValue = new PropertyValue(true); + UpdateImage(ImageVisualProperty.CropToMask, setCropValue); + } } - - _alphaMaskUrl = value; - - PropertyValue setValue = new PropertyValue(value); - UpdateImage(ImageVisualProperty.AlphaMaskURL, setValue); setValue?.Dispose(); } } @@ -665,15 +873,10 @@ namespace Tizen.NUI.BaseComponents get { bool ret = false; - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue cropUrl = imageMap?.Find(ImageVisualProperty.CropToMask); - cropUrl?.Get(out ret); - imageMap?.Dispose(); - image?.Dispose(); - cropUrl?.Dispose(); + PropertyValue cropToMask = GetCachedImageVisualProperty(ImageVisualProperty.CropToMask); + cropToMask?.Get(out ret); + cropToMask?.Dispose(); return ret; } @@ -681,11 +884,31 @@ namespace Tizen.NUI.BaseComponents { PropertyValue setValue = new PropertyValue(value); UpdateImage(ImageVisualProperty.CropToMask, setValue); - setValue.Dispose(); + setValue?.Dispose(); } } - internal VisualFittingModeType CovertFittingModetoVisualFittingMode(FittingModeType value) + /// + /// Actions property value for Reload image. + /// + internal static readonly int ActionReload = Interop.ImageView.ImageVisualActionReloadGet(); + + /// + /// Actions property value to Play animated images. + /// + internal static readonly int ActionPlay = Interop.AnimatedImageView.AnimatedImageVisualActionPlayGet(); + + /// + /// Actions property value to Pause animated images. + /// + internal static readonly int ActionPause = Interop.AnimatedImageView.AnimatedImageVisualActionPauseGet(); + + /// + /// Actions property value to Stop animated images. + /// + internal static readonly int ActionStop = Interop.AnimatedImageView.AnimatedImageVisualActionStopGet(); + + internal VisualFittingModeType ConvertFittingModetoVisualFittingMode(FittingModeType value) { switch (value) { @@ -700,7 +923,7 @@ namespace Tizen.NUI.BaseComponents case FittingModeType.FitHeight: return VisualFittingModeType.FitHeight; case FittingModeType.FitWidth: - return VisualFittingModeType.FitHeight; + return VisualFittingModeType.FitWidth; default: return VisualFittingModeType.Fill; } @@ -757,35 +980,23 @@ namespace Tizen.NUI.BaseComponents { get { - int ret = (int)_fittingMode; - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue fittingMode = imageMap?.Find(Visual.Property.VisualFittingMode); - fittingMode?.Get(out ret); - _fittingMode = (VisualFittingModeType)ret; + int ret = (int)VisualFittingModeType.Fill; - imageMap?.Dispose(); - image?.Dispose(); + PropertyValue fittingMode = GetCachedImageVisualProperty(Visual.Property.VisualFittingMode); + fittingMode?.Get(out ret); fittingMode?.Dispose(); return ConvertVisualFittingModetoFittingMode((VisualFittingModeType)ret); } set { - VisualFittingModeType ret = CovertFittingModetoVisualFittingMode(value); + VisualFittingModeType ret = ConvertFittingModetoVisualFittingMode(value); PropertyValue setValue = new PropertyValue((int)ret); - if(_fittingMode != ret) - { - _fittingMode = ret; - UpdateImage(Visual.Property.VisualFittingMode, setValue); - } + UpdateImage(Visual.Property.VisualFittingMode, setValue); setValue?.Dispose(); } } - - /// /// Gets or sets the desired image width.
/// If not specified, the actual image width is used.
@@ -810,15 +1021,14 @@ namespace Tizen.NUI.BaseComponents { get { - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue desireWidth = imageMap?.Find(ImageVisualProperty.DesiredWidth); - desireWidth?.Get(out _desired_width); - - imageMap?.Dispose(); - image?.Dispose(); - desireWidth?.Dispose(); + // Sync as current properties only if both _desired_width and _desired_height are setuped. + if (_desired_width != -1 && _desired_height != -1) + { + UpdateImage(); + } + PropertyValue desirewidth = GetCachedImageVisualProperty(ImageVisualProperty.DesiredWidth); + desirewidth?.Get(out _desired_width); + desirewidth?.Dispose(); return _desired_width; } @@ -827,7 +1037,9 @@ namespace Tizen.NUI.BaseComponents if (_desired_width != value) { _desired_width = value; - UpdateImage(0, null); + PropertyValue setValue = new PropertyValue(value); + UpdateImage(ImageVisualProperty.DesiredWidth, setValue, false); + setValue?.Dispose(); } } } @@ -856,14 +1068,13 @@ namespace Tizen.NUI.BaseComponents { get { - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue desireheight = imageMap?.Find(ImageVisualProperty.DesiredHeight); + // Sync as current properties only if both _desired_width and _desired_height are setuped. + if (_desired_width != -1 && _desired_height != -1) + { + UpdateImage(); + } + PropertyValue desireheight = GetCachedImageVisualProperty(ImageVisualProperty.DesiredHeight); desireheight?.Get(out _desired_height); - - imageMap?.Dispose(); - image?.Dispose(); desireheight?.Dispose(); return _desired_height; @@ -873,7 +1084,9 @@ namespace Tizen.NUI.BaseComponents if (_desired_height != value) { _desired_height = value; - UpdateImage(0, null); + PropertyValue setValue = new PropertyValue(value); + UpdateImage(ImageVisualProperty.DesiredHeight, setValue, false); + setValue?.Dispose(); } } } @@ -895,20 +1108,15 @@ namespace Tizen.NUI.BaseComponents NotifyPropertyChanged(); } } - + private ReleasePolicyType InternalReleasePolicy { get { int ret = (int)ReleasePolicyType.Detached; - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue releasePoli = imageMap?.Find(ImageVisualProperty.ReleasePolicy); - releasePoli?.Get(out ret); - imageMap?.Dispose(); - image?.Dispose(); + PropertyValue releasePoli = GetCachedImageVisualProperty(ImageVisualProperty.ReleasePolicy); + releasePoli?.Get(out ret); releasePoli?.Dispose(); return (ReleasePolicyType)ret; @@ -948,15 +1156,10 @@ namespace Tizen.NUI.BaseComponents get { int ret = (int)WrapModeType.Default; - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue warpModeU = imageMap?.Find(ImageVisualProperty.WrapModeU); - warpModeU?.Get(out ret); - imageMap?.Dispose(); - image?.Dispose(); - warpModeU?.Dispose(); + PropertyValue wrapModeU = GetCachedImageVisualProperty(ImageVisualProperty.WrapModeU); + wrapModeU?.Get(out ret); + wrapModeU?.Dispose(); return (WrapModeType)ret; } @@ -996,14 +1199,9 @@ namespace Tizen.NUI.BaseComponents get { int ret = (int)WrapModeType.Default; - PropertyMap imageMap = new PropertyMap(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - PropertyValue wrapModeV = imageMap?.Find(ImageVisualProperty.WrapModeV); - wrapModeV?.Get(out ret); - imageMap?.Dispose(); - image?.Dispose(); + PropertyValue wrapModeV = GetCachedImageVisualProperty(ImageVisualProperty.WrapModeV); + wrapModeV?.Get(out ret); wrapModeV?.Dispose(); return (WrapModeType)ret; @@ -1018,10 +1216,16 @@ namespace Tizen.NUI.BaseComponents /// /// Gets or sets the mode to adjust view size to preserve the aspect ratio of the image resource. - /// If this is set to be true, then the width or height, which is not set by user explicitly, can be adjusted to preserve the aspect ratio of the image resource. - /// AdjustViewSize works only if ImageView is added to a View having Layout. /// - [EditorBrowsable(EditorBrowsableState.Never)] + /// + /// This is false by default. + /// If this is set to be true, then the width or height value, which is not set by user explicitly, can be changed automatically + /// to preserve the aspect ratio of the image resource. + /// AdjustViewSize works only if ImageView is added to a View having Layout. + /// e.g. If the image resource size is (100, 100), then the ImageView requests size (100, 100) to its parent layout by default. + /// If the ImageView's HeightSpecification is 50 and AdjustViewSize is true, then the ImageView requests size (50, 50) instead of (100, 50). + /// + /// 9 public bool AdjustViewSize { get @@ -1036,6 +1240,44 @@ namespace Tizen.NUI.BaseComponents } private bool adjustViewSize = false; + /// + /// ImageView PlaceHolderUrl, type string. + /// This is one of mandatory property. Even if not set or null set, it sets empty string ("") internally. + /// When it is set as null, it gives empty string ("") to be read. + /// + /// 11 + [EditorBrowsable(EditorBrowsableState.Never)] + public string PlaceHolderUrl + { + get + { + return (string)GetValue(PlaceHolderUrlProperty); + } + set + { + SetValue(PlaceHolderUrlProperty, value); + NotifyPropertyChanged(); + } + } + + /// + /// Gets or sets whether the image use TransitionEffect or not
+ ///
+ /// 11 + [EditorBrowsable(EditorBrowsableState.Never)] + public bool TransitionEffect + { + get + { + return (bool)GetValue(TransitionEffectProperty); + } + set + { + SetValue(TransitionEffectProperty, value); + NotifyPropertyChanged(); + } + } + internal Selector ResourceUrlSelector { get => GetSelector(resourceUrlSelector, ImageView.ResourceUrlProperty); @@ -1062,14 +1304,31 @@ namespace Tizen.NUI.BaseComponents { if (url.Contains(".json")) { - Tizen.Log.Fatal("NUI", "[ERROR] Please DO NOT set lottie file in ImageView! This is temporary checking, will be removed soon!"); + Tizen.Log.Fatal("NUI", "[ERROR] Do not set lottie file in ImageView! This is temporary checking, will be removed soon!"); return; } - Interop.ImageView.SetImage(SwigCPtr, url, Uint16Pair.getCPtr(size)); + Interop.ImageView.SetImage(SwigCPtr, ConvertResourceUrl(ref url), Uint16Pair.getCPtr(size)); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); - ResourceUrl = url; + _resourceUrl = url; + _desired_width = size?.GetWidth() ?? -1; + _desired_height = size?.GetHeight() ?? -1; + + // Update cached property. Note that we should not re-create new visual. + using (PropertyValue urlValue = new PropertyValue(_resourceUrl)) + { + UpdateImage(ImageVisualProperty.URL, urlValue, false); + } + using (PropertyValue desiredWidthValue = new PropertyValue(_desired_width)) + { + UpdateImage(ImageVisualProperty.DesiredWidth, desiredWidthValue, false); + } + using (PropertyValue desiredHeightValue = new PropertyValue(_desired_height)) + { + UpdateImage(ImageVisualProperty.DesiredWidth, desiredHeightValue, false); + } + imagePropertyUpdatedFlag = false; } internal ViewResourceReadySignal ResourceReadySignal(View view) @@ -1083,36 +1342,28 @@ namespace Tizen.NUI.BaseComponents { base.ApplyCornerRadius(); - UpdateImage(0, null); + if (backgroundExtraData == null) return; + + + // Update corner radius properties to image by ActionUpdateProperty + if (backgroundExtraData.CornerRadius != null) + { + Interop.View.InternalUpdateVisualPropertyVector4(this.SwigCPtr, ImageView.Property.IMAGE, Visual.Property.CornerRadius, Vector4.getCPtr(backgroundExtraData.CornerRadius)); + } + Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, Visual.Property.CornerRadiusPolicy, (int)backgroundExtraData.CornerRadiusPolicy); } internal override void ApplyBorderline() { base.ApplyBorderline(); - // Apply borderline to IMAGE. - if (backgroundExtraData != null) - { - var borderlineColor = backgroundExtraData.BorderlineColor == null ? new PropertyValue(Color.Black) : new PropertyValue(backgroundExtraData.BorderlineColor); + if (backgroundExtraData == null) return; - // Apply to the image visual - PropertyMap imageMap = new PropertyMap(); - PropertyValue imageValue = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - if (imageValue.Get(imageMap) && !imageMap.Empty()) - { - imageMap[Visual.Property.BorderlineWidth] = new PropertyValue(backgroundExtraData.BorderlineWidth); - imageMap[Visual.Property.BorderlineColor] = borderlineColor; - imageMap[Visual.Property.BorderlineOffset] = new PropertyValue(backgroundExtraData.BorderlineOffset); - var temp = new PropertyValue(imageMap); - Tizen.NUI.Object.SetProperty(SwigCPtr, ImageView.Property.IMAGE, temp); - temp.Dispose(); - } - imageMap.Dispose(); - imageValue.Dispose(); - borderlineColor.Dispose(); - } - UpdateImage(0, null); + // Update borderline properties to image by ActionUpdateProperty + Interop.View.InternalUpdateVisualPropertyFloat(this.SwigCPtr, ImageView.Property.IMAGE, Visual.Property.BorderlineWidth, backgroundExtraData.BorderlineWidth); + Interop.View.InternalUpdateVisualPropertyVector4(this.SwigCPtr, ImageView.Property.IMAGE, Visual.Property.BorderlineColor, Vector4.getCPtr(backgroundExtraData.BorderlineColor ?? Color.Black)); + Interop.View.InternalUpdateVisualPropertyFloat(this.SwigCPtr, ImageView.Property.IMAGE, Visual.Property.BorderlineOffset, backgroundExtraData.BorderlineOffset); } internal ResourceLoadingStatusType GetResourceStatus() @@ -1132,6 +1383,8 @@ namespace Tizen.NUI.BaseComponents return; } + internalPixelArea?.Dispose(); + if (type == DisposeTypes.Explicit) { //Called by User @@ -1141,6 +1394,14 @@ namespace Tizen.NUI.BaseComponents _border = null; borderSelector?.Reset(this); resourceUrlSelector?.Reset(this); + imagePropertyUpdatedFlag = false; + if (imagePropertyUpdateProcessAttachedFlag) + { + ProcessorController.Instance.ProcessorOnceEvent -= UpdateImage; + imagePropertyUpdateProcessAttachedFlag = false; + } + cachedImagePropertyMap?.Dispose(); + cachedImagePropertyMap = null; } base.Dispose(type); @@ -1156,6 +1417,11 @@ namespace Tizen.NUI.BaseComponents // Callback for View ResourceReady signal private void OnResourceReady(IntPtr data) { + if (!CheckResourceReady()) + { + return; + } + ResourceReadyEventArgs e = new ResourceReadyEventArgs(); if (data != null) { @@ -1170,14 +1436,27 @@ namespace Tizen.NUI.BaseComponents private void SetResourceUrl(string value) { - value = (value == null ? "" : value); - if (value.StartsWith("*Resource*")) + if (_resourceUrl != ConvertResourceUrl(ref value)) { - string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource; - value = value.Replace("*Resource*", resource); + _resourceUrl = value; + if (string.IsNullOrEmpty(_resourceUrl)) + { + // Special case. If we set ResourceUrl as empty, Unregist visual. + RemoveImage(); + } + else + { + using (PropertyValue setValue = new PropertyValue(value)) + { + UpdateImage(ImageVisualProperty.URL, setValue); + } + // Special case. If we set GeneratedUrl, or FastTrackUploading, Create ImageVisual synchronously. + if (value.StartsWith("dali://") || value.StartsWith("enbuf://") || _fastTrackUploading) + { + UpdateImage(); + } + } } - _resourceUrl = value; - UpdateImage(ImageVisualProperty.URL, new PropertyValue(value)); } private void SetBorder(Rectangle value) @@ -1186,81 +1465,159 @@ namespace Tizen.NUI.BaseComponents { return; } - _border = new Rectangle(value); - UpdateImage(NpatchImageVisualProperty.Border, new PropertyValue(_border)); + if (_border != value) + { + _border = new Rectangle(value); + UpdateImage(NpatchImageVisualProperty.Border, new PropertyValue(_border)); + } } - private void UpdateImageMap(PropertyMap fromMap) + /// + /// Unregist image visual directly. After this operation, we cannot get any properties from Image property. + /// + private void RemoveImage() { - PropertyMap imageMap = new PropertyMap(); + // If previous resourceUrl was already empty, we don't need to do anything. just ignore. + // Unregist and detach process only if previous resourceUrl was not empty + string currentResourceUrl = ""; + PropertyValue currentResourceUrlValue = GetCachedImageVisualProperty(ImageVisualProperty.URL); + if ((currentResourceUrlValue?.Get(out currentResourceUrl) ?? false) && !string.IsNullOrEmpty(currentResourceUrl)) + { + PropertyValue emptyValue = new PropertyValue(); - PropertyValue image = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE); - image?.Get(imageMap); - imageMap?.Merge(fromMap); - PropertyValue setValue = new PropertyValue(imageMap); - SetProperty(ImageView.Property.IMAGE, setValue); + // Remove current registed Image. + SetProperty(ImageView.Property.IMAGE, emptyValue); - imageMap?.Dispose(); - image?.Dispose(); - setValue?.Dispose(); + // Image visual is not exist anymore. We should ignore lazy UpdateImage + imagePropertyUpdatedFlag = false; + if (imagePropertyUpdateProcessAttachedFlag) + { + ProcessorController.Instance.ProcessorOnceEvent -= UpdateImage; + imagePropertyUpdateProcessAttachedFlag = false; + } + // Update resourceUrl as empty value + cachedImagePropertyMap[ImageVisualProperty.URL] = emptyValue; + + emptyValue?.Dispose(); + } + currentResourceUrlValue?.Dispose(); } - private void UpdateImage(int key, PropertyValue value) + /// + /// Lazy call to UpdateImage. + /// Collect Properties need to be update, and set properties that starts the Processing. + /// + /// If you want to update cachedImagePropertyMap, but don't want to request new visual creation, make requiredVisualCreation value as false. + /// (Example : if we change SynchronousLoading property from 'true' to 'false', or if we call this function during UpdateImage) + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void UpdateImage(int key, PropertyValue value, bool requiredVisualCreation = true) { - PropertyMap imageMap = new PropertyMap(); - - if (_alphaMaskUrl != null) + // Update image property map value as inputed value. + if (key != 0) { - PropertyValue alphaMaskUrl = new PropertyValue(_alphaMaskUrl); - imageMap?.Insert(ImageVisualProperty.AlphaMaskURL, alphaMaskUrl); - alphaMaskUrl?.Dispose(); - } + if (!HasBody()) + { + // Throw exception if ImageView is disposed. + throw new global::System.InvalidOperationException("[NUI][ImageVIew] Someone try to change disposed ImageView's property.\n"); + } - if (string.IsNullOrEmpty(_resourceUrl)) - { - PropertyValue resourceUrl = new PropertyValue(_resourceUrl); - imageMap?.Insert(ImageVisualProperty.URL, resourceUrl); - PropertyValue setValue = new PropertyValue(imageMap); - SetProperty(ImageView.Property.IMAGE, setValue); - resourceUrl?.Dispose(); - setValue?.Dispose(); - return; - } + if (cachedImagePropertyMap == null) + { + cachedImagePropertyMap = new PropertyMap(); + } - if (_border == null) - { - PropertyValue image = new PropertyValue((int)Visual.Type.Image); - imageMap?.Insert(Visual.Property.Type, image); - image?.Dispose(); + // To optimization, we don't check URL duplicate case. We already checked before. + if (key != ImageVisualProperty.URL) + { + using (PropertyValue oldValue = GetCachedImageVisualProperty(key)) + { + if (oldValue != null && oldValue.EqualTo(value)) + { + // Ignore UpdateImage query when we try to update equality value. + return; + } + } + } + imagePropertyUpdatedFlag = true; + cachedImagePropertyMap[key] = value; + + // Lazy update only if visual creation required, and _resourceUrl is not empty, and ProcessAttachedFlag is false. + if (requiredVisualCreation && !string.IsNullOrEmpty(_resourceUrl) && !imagePropertyUpdateProcessAttachedFlag) + { + imagePropertyUpdateProcessAttachedFlag = true; + ProcessorController.Instance.ProcessorOnceEvent += UpdateImage; + // Call process hardly. + ProcessorController.Instance.Awake(); + } } - else + } + + /// + /// Callback function to Lazy UpdateImage. + /// + private void UpdateImage(object source, EventArgs e) + { + // Note : To allow event attachment during UpdateImage, let we make flag as false before call UpdateImage(). + imagePropertyUpdateProcessAttachedFlag = false; + UpdateImage(); + } + + /// + /// Update image-relative properties synchronously. + /// After call this API, All image properties updated. + /// + /// + /// Current version ImageView property update asynchronously. + /// If you want to guarantee that ImageView property setuped, + /// Please call this ImageView.UpdateImage() API. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void UpdateImage() + { + if (!imagePropertyUpdatedFlag) return; + + imagePropertyUpdatedFlag = false; + + if (cachedImagePropertyMap == null) { - PropertyValue nPatch = new PropertyValue((int)Visual.Type.NPatch); - imageMap?.Insert(Visual.Property.Type, nPatch); - nPatch?.Dispose(); - PropertyValue border = new PropertyValue(_border); - imageMap?.Insert(NpatchImageVisualProperty.Border, border); - border?.Dispose(); + cachedImagePropertyMap = new PropertyMap(); } - if(key != Visual.Property.VisualFittingMode && _fittingMode != VisualFittingModeType.Fill) + // Checkup the cached visual type is AnimatedImage. + // It is trick to know that this code is running on AnimatedImageView.UpdateImage() with resourceURLs or not. + int visualType = (int)Visual.Type.Invalid; + if (!((GetCachedImageVisualProperty(Visual.Property.Type)?.Get(out visualType) ?? false) && (visualType == (int)Visual.Type.AnimatedImage))) { - PropertyValue fittingMode = new PropertyValue((int)_fittingMode); - imageMap?.Insert(Visual.Property.VisualFittingMode, fittingMode); - fittingMode?.Dispose(); + // If ResourceUrl is not setuped, don't set property. fast return. + if (string.IsNullOrEmpty(_resourceUrl)) + { + return; + } + if (_border == null) + { + PropertyValue image = new PropertyValue((int)Visual.Type.Image); + cachedImagePropertyMap[Visual.Property.Type] = image; + image?.Dispose(); + } + else + { + PropertyValue nPatch = new PropertyValue((int)Visual.Type.NPatch); + cachedImagePropertyMap[Visual.Property.Type] = nPatch; + nPatch?.Dispose(); + PropertyValue border = new PropertyValue(_border); + cachedImagePropertyMap[NpatchImageVisualProperty.Border] = border; + border?.Dispose(); + } } - PropertyValue synchronousLoading = new PropertyValue(_synchronousLoading); - imageMap?.Insert(NpatchImageVisualProperty.SynchronousLoading, synchronousLoading); - synchronousLoading?.Dispose(); - if (backgroundExtraData != null && backgroundExtraData.CornerRadius != null) { using (var cornerRadius = new PropertyValue(backgroundExtraData.CornerRadius)) using (var cornerRadiusPolicy = new PropertyValue((int)backgroundExtraData.CornerRadiusPolicy)) { - imageMap.Insert(Visual.Property.CornerRadius, cornerRadius); - imageMap.Insert(Visual.Property.CornerRadiusPolicy, new PropertyValue((int)(backgroundExtraData.CornerRadiusPolicy))); + cachedImagePropertyMap[Visual.Property.CornerRadius] = cornerRadius; + cachedImagePropertyMap[Visual.Property.CornerRadiusPolicy] = new PropertyValue((int)(backgroundExtraData.CornerRadiusPolicy)); } } @@ -1270,24 +1627,20 @@ namespace Tizen.NUI.BaseComponents using (var borderlineColor = new PropertyValue(backgroundExtraData.BorderlineColor)) using (var borderlineOffset = new PropertyValue(backgroundExtraData.BorderlineOffset)) { - imageMap.Insert(Visual.Property.BorderlineWidth, borderlineWidth); - imageMap.Insert(Visual.Property.BorderlineColor, borderlineColor); - imageMap.Insert(Visual.Property.BorderlineOffset, borderlineOffset); + cachedImagePropertyMap[Visual.Property.BorderlineWidth] = borderlineWidth; + cachedImagePropertyMap[Visual.Property.BorderlineColor] = borderlineColor; + cachedImagePropertyMap[Visual.Property.BorderlineOffset] = borderlineOffset; } } - if (value != null) - { - imageMap?.Insert(key, value); - } - // Do Fitting Buffer when desired dimension is set + // TODO : Couldn't we do this job in dali-engine side. if (_desired_width != -1 && _desired_height != -1) { if (_resourceUrl != null) { Size2D imageSize = ImageLoader.GetOriginalImageSize(_resourceUrl, true); - if( imageSize.Height > 0 && imageSize.Width > 0 && _desired_width > 0 && _desired_height > 0 ) + if (imageSize.Height > 0 && imageSize.Width > 0 && _desired_width > 0 && _desired_height > 0) { int adjustedDesiredWidth, adjustedDesiredHeight; float aspectOfDesiredSize = (float)_desired_height / (float)_desired_width; @@ -1304,32 +1657,131 @@ namespace Tizen.NUI.BaseComponents } PropertyValue returnWidth = new PropertyValue(adjustedDesiredWidth); - imageMap?.Insert(ImageVisualProperty.DesiredWidth, returnWidth); + cachedImagePropertyMap[ImageVisualProperty.DesiredWidth] = returnWidth; returnWidth?.Dispose(); PropertyValue returnHeight = new PropertyValue(adjustedDesiredHeight); - imageMap?.Insert(ImageVisualProperty.DesiredHeight, returnHeight); + cachedImagePropertyMap[ImageVisualProperty.DesiredHeight] = returnHeight; returnHeight?.Dispose(); PropertyValue scaleToFit = new PropertyValue((int)FittingModeType.ScaleToFill); - imageMap?.Insert(ImageVisualProperty.FittingMode, scaleToFit); + cachedImagePropertyMap[ImageVisualProperty.FittingMode] = scaleToFit; scaleToFit?.Dispose(); } - else - { - Tizen.Log.Fatal("NUI", "[ERROR] Can't use DesiredSize when ImageLoading is failed."); - } imageSize?.Dispose(); } } - UpdateImageMap(imageMap); + UpdateImageMap(); + } + + /// + /// Merge our collected properties, and set IMAGE property internally. + /// + private void UpdateImageMap() + { + // Note : We can't use ImageView.Image property here. Because That property call UpdateImage internally. + using (PropertyMap imageMap = new PropertyMap()) + { + using (PropertyValue returnValue = Tizen.NUI.Object.GetProperty(SwigCPtr, ImageView.Property.IMAGE)) + { + returnValue?.Get(imageMap); + } + if (cachedImagePropertyMap != null) + { + imageMap?.Merge(cachedImagePropertyMap); + } + using (PropertyValue setValue = new PropertyValue(imageMap)) + { + SetProperty(ImageView.Property.IMAGE, setValue); + } + + // Update cached image property. + MergeCachedImageVisualProperty(imageMap); + } + } + + /// + /// Get image visual property by key. + /// If we found value in local Cached result, return that. + /// Else, get synced native map and return that. + /// If there is no matched value, return null. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual PropertyValue GetImageVisualProperty(int key) + { + PropertyValue ret = GetCachedImageVisualProperty(key); + if (ret == null) + { + // If we cannot find result form cached map, Get value from native engine. + ret = Image?.Find(key); + } + return ret; + } + + /// + /// Get image visual property from NUI cached image map by key. + /// If there is no matched value, return null. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual PropertyValue GetCachedImageVisualProperty(int key) + { + return cachedImagePropertyMap?.Find(key); + } + + /// + /// Update NUI cached image visual property map by inputed property map. + /// + /// + /// For performance issue, we will collect only "cachedImagePropertyKeyList" hold. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void MergeCachedImageVisualProperty(PropertyMap map) + { + if (map == null) return; + if (cachedImagePropertyMap == null) + { + cachedImagePropertyMap = new PropertyMap(); + } + foreach (var key in cachedImagePropertyKeyList) + { + PropertyValue value = map.Find(key); + if (value != null) + { + // Update-or-Insert new value + cachedImagePropertyMap[key] = value; + if (key == ImageVisualProperty.URL) + { + // Special case. If key is Url, update _resourceUrl here. + value.Get(out _resourceUrl); + } + } + } + } - imageMap?.Dispose(); - imageMap = null; + /// + /// GetNaturalSize() should be guaranteed that ResourceUrl property setuped. + /// So before get base.GetNaturalSize(), we should synchronous image properties + /// + internal override Vector3 GetNaturalSize() + { + // Sync as current properties + UpdateImage(); + return base.GetNaturalSize(); } + [EditorBrowsable(EditorBrowsableState.Never)] + protected override bool CheckResourceReady() + { + // If we have some properties to be updated, this signal is old thing. + // We need to ignore current signal, and wait next. + return !(imagePropertyUpdateProcessAttachedFlag && imagePropertyUpdatedFlag); + } private void OnResourceLoaded(IntPtr view) { + if (!CheckResourceReady()) + { + return; + } ResourceLoadedEventArgs e = new ResourceLoadedEventArgs(); e.Status = (ResourceLoadingStatusType)Interop.View.GetVisualResourceStatus(this.SwigCPtr, Property.IMAGE); @@ -1385,10 +1837,8 @@ namespace Tizen.NUI.BaseComponents internal static readonly int IMAGE = Interop.ImageView.ImageGet(); internal static readonly int PreMultipliedAlpha = Interop.ImageView.PreMultipliedAlphaGet(); internal static readonly int PixelArea = Interop.ImageView.PixelAreaGet(); - internal static readonly int ActionReload = Interop.ImageView.ImageVisualActionReloadGet(); - internal static readonly int ActionPlay = Interop.ImageView.ImageVisualActionPlayGet(); - internal static readonly int ActionPause = Interop.ImageView.ImageVisualActionPauseGet(); - internal static readonly int ActionStop = Interop.ImageView.ImageVisualActionStopGet(); + internal static readonly int PlaceHolderUrl = Interop.ImageView.PlaceHolderImageGet(); + internal static readonly int TransitionEffect = Interop.ImageView.TransitionEffectGet(); } private enum ImageType