/* * Copyright(c) 2019 Samsung Electronics Co., Ltd. * * 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.Collections.Generic; using System.Runtime.InteropServices; using System.ComponentModel; using Tizen.NUI.Binding; namespace Tizen.NUI.BaseComponents { /// /// ImageView is a class for displaying an image resource.
/// An instance of ImageView can be created using a URL or an image instance.
///
/// 3 public partial class ImageView : View { static ImageView() { } private EventHandler _resourceReadyEventHandler; private ResourceReadyEventCallbackType _resourceReadyEventCallback; 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 int _desired_width = -1; private int _desired_height = -1; private bool _fastTrackUploading = false; private TriggerableSelector resourceUrlSelector; private TriggerableSelector borderSelector; private RelativeVector4 internalPixelArea; /// /// Creates an initialized ImageView. /// /// 3 public ImageView() : this(Interop.ImageView.New(), true) { if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); } /// 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(ViewStyle viewStyle) : this(Interop.ImageView.New(), true, viewStyle) { } /// /// Creates an initialized ImageView with setting the status of shown or hidden. /// /// 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(bool shown) : this(Interop.ImageView.New(), true) { if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); SetVisible(shown); } /// /// Creates an initialized ImageView from a URL to an image resource.
/// If the string is empty, ImageView will not display anything.
///
/// The URL of the image resource to display. /// 3 public ImageView(string url) : this(Interop.ImageView.New(ConvertResourceUrl(ref url)), true) { _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(); } /// /// Creates an initialized ImageView from a URL to an image resource with setting shown or hidden. /// /// The URL of the image resource to display. /// 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(ConvertResourceUrl(ref url)), true) { _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(ConvertResourceUrl(ref url), Uint16Pair.getCPtr(size)), true) { _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) { SetVisible(false); } } internal ImageView(global::System.IntPtr cPtr, bool cMemoryOwn, ViewStyle viewStyle, bool shown = true) : base(cPtr, cMemoryOwn, viewStyle) { if (!shown) { SetVisible(false); } } internal ImageView(global::System.IntPtr cPtr, bool cMemoryOwn, bool shown = true) : base(cPtr, cMemoryOwn, null) { if (!shown) { SetVisible(false); } } [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void ResourceReadyEventCallbackType(IntPtr data); [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void _resourceLoadedCallbackType(IntPtr view); /// /// An event for ResourceReady signal which can be used to subscribe or unsubscribe the event handler.
/// This signal is emitted after all resources required by a control are loaded and ready.
/// Most resources are only loaded when the control is placed on the stage.
///
/// 3 public event EventHandler ResourceReady { add { if (_resourceReadyEventHandler == null) { _resourceReadyEventCallback = OnResourceReady; ViewResourceReadySignal resourceReadySignal = ResourceReadySignal(this); resourceReadySignal?.Connect(_resourceReadyEventCallback); resourceReadySignal?.Dispose(); } _resourceReadyEventHandler += value; } remove { _resourceReadyEventHandler -= value; ViewResourceReadySignal resourceReadySignal = ResourceReadySignal(this); if (_resourceReadyEventHandler == null && resourceReadySignal?.Empty() == false) { resourceReadySignal?.Disconnect(_resourceReadyEventCallback); } resourceReadySignal?.Dispose(); } } internal event EventHandler ResourceLoaded { add { if (_resourceLoadedEventHandler == null) { _resourceLoadedCallback = OnResourceLoaded; ViewResourceReadySignal resourceReadySignal = this.ResourceReadySignal(this); resourceReadySignal?.Connect(_resourceLoadedCallback); resourceReadySignal?.Dispose(); } _resourceLoadedEventHandler += value; } remove { _resourceLoadedEventHandler -= value; ViewResourceReadySignal resourceReadySignal = this.ResourceReadySignal(this); if (_resourceLoadedEventHandler == null && resourceReadySignal?.Empty() == false) { resourceReadySignal?.Disconnect(_resourceLoadedCallback); } resourceReadySignal?.Dispose(); } } /// /// Enumeration for LoadingStatus of image. /// /// 5 public enum LoadingStatusType { /// /// Loading preparing status. /// /// 5 Preparing, /// /// Loading ready status. /// /// 5 Ready, /// /// Loading failed status. /// /// 5 Failed } /// /// 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. /// /// 3 public string ResourceUrl { get { return (string)GetValue(ResourceUrlProperty); } set { SetValue(ResourceUrlProperty, value); NotifyPropertyChanged(); } } /// /// This will be deprecated, Use Image instead.
/// ImageView ImageMap, type PropertyMap: string if it is a URL, map otherwise. ///
/// 3 [Obsolete("Do not use this, that will be deprecated. Use Image property instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public PropertyMap ImageMap { get { return GetValue(ImageMapProperty) as PropertyMap; } set { SetValue(ImageMapProperty, value); NotifyPropertyChanged(); } } private PropertyMap InternalImageMap { get { 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 { return null; } } set { if (_border == null) { 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(); } } } /// /// ImageView Image, type PropertyMap: string if it is a URL, map otherwise. /// /// /// This PropertyMap use a .
/// See for a detailed description.
/// you can also use .
/// See for a detailed description.
///
/// /// The following example demonstrates how to use the Image property. /// /// PropertyMap map = new PropertyMap(); /// map.Insert(Visual.Property.Type, new PropertyValue((int)Visual.Type.Image)); /// map.Insert(ImageVisualProperty.AlphaMaskURL, new PropertyValue(url)); /// map.Insert(ImageVisualProperty.FittingMode, new PropertyValue((int)FittingModeType.ScaleToFill); /// imageview.Image = map; /// /// /// 4 public PropertyMap Image { get { if (_border == null) { return (PropertyMap)GetValue(ImageProperty); } else { return null; } } set { if (_border == null) { SetValue(ImageProperty, value); NotifyPropertyChanged(); } } } /// /// ImageView PreMultipliedAlpha, type Boolean.
/// Image must be initialized.
///
/// 3 public bool PreMultipliedAlpha { get { return (bool)GetValue(PreMultipliedAlphaProperty); } set { SetValue(PreMultipliedAlphaProperty, value); NotifyPropertyChanged(); } } /// /// ImageView PixelArea, type Vector4 (Animatable property).
/// Pixel area is a relative value with the whole image area as [0.0, 0.0, 1.0, 1.0].
///
/// /// The property cascade chaining set is possible. For example, this (imageView.PixelArea.X = 0.1f;) is possible. /// /// 3 public RelativeVector4 PixelArea { get { return (RelativeVector4)GetValue(PixelAreaProperty); } set { SetValue(PixelAreaProperty, value); NotifyPropertyChanged(); } } /// /// The border of the image in the order: left, right, bottom, top.
/// If set, ImageMap will be ignored.
/// For N-Patch images only.
/// Optional. ///
/// /// The property cascade chaining set is possible. For example, this (imageView.Border.X = 1;) is possible. /// /// 3 public Rectangle Border { get { Rectangle temp = (Rectangle)GetValue(BorderProperty); if (null == temp) { return null; } else { return new Rectangle(OnBorderChanged, temp.X, temp.Y, temp.Width, temp.Height); } } set { SetValue(BorderProperty, value); NotifyPropertyChanged(); } } /// /// Gets or sets whether to draw the borders only (if true).
/// If not specified, the default is false.
/// For N-Patch images only.
/// Optional. ///
/// 3 public bool BorderOnly { get { return (bool)GetValue(BorderOnlyProperty); } set { SetValue(BorderOnlyProperty, value); NotifyPropertyChanged(); } } /// /// Gets or sets whether to synchronous loading the resourceurl of image.
///
/// 3 [Obsolete("This has been deprecated since API9 and will be removed in API11. Use SynchronousLoading instead.")] public bool SynchronosLoading { get { return SynchronousLoading; } set { SynchronousLoading = value; } } /// /// Gets or sets whether the image of the ResourceUrl property will be loaded synchronously.
///
/// /// Changing this property make this ImageView load image synchronously at the next loading /// by following operation: , , /// and by some properties those cause reloading: , and etc. /// /// 9 public bool SynchronousLoading { get { return (bool)GetValue(SynchronousLoadingProperty); } set { SetValue(SynchronousLoadingProperty, value); NotifyPropertyChanged(); } } /// /// Gets or sets whether to automatically correct the orientation of an image.
///
/// 5 public bool OrientationCorrection { get { return (bool)GetValue(OrientationCorrectionProperty); } set { SetValue(OrientationCorrectionProperty, value); NotifyPropertyChanged(); } } /// /// 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 public ImageView.LoadingStatusType LoadingStatus { get { return (ImageView.LoadingStatusType)Interop.View.GetVisualResourceStatus(SwigCPtr, (int)Property.IMAGE); } } /// /// Downcasts a handle to imageView handle. /// /// Thrown when handle is null. /// Do not use this, that will be deprecated. Use as keyword instead. /// 3 [Obsolete("Do not use this, that will be deprecated. Use as keyword instead. " + "Like: " + "BaseHandle handle = new ImageView(imagePath); " + "ImageView image = handle as ImageView")] [EditorBrowsable(EditorBrowsableState.Never)] public static ImageView DownCast(BaseHandle handle) { if (null == handle) { throw new ArgumentNullException(nameof(handle)); } ImageView ret = Registry.GetManagedBaseHandleFromNativePtr(handle) as ImageView; if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); return ret; } /// /// Sets this ImageView from the given URL.
/// If the URL is empty, ImageView will not display anything.
///
/// The URL to the image resource to display. /// Thrown when url is null. /// 3 public void SetImage(string url) { if (null == url) { throw new ArgumentNullException(nameof(url)); } if (url.Contains(".json")) { 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, ConvertResourceUrl(ref url)); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); _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; } /// /// Queries if all resources required by a control are loaded and ready.
/// Most resources are only loaded when the control is placed on the stage.
/// True if the resources are loaded and ready, false otherwise.
///
/// 3 public new bool IsResourceReady() { bool ret = Interop.View.IsResourceReady(SwigCPtr); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); return ret; } /// /// Forcefully reloads the image. All the visuals using this image will reload to the latest image. /// /// 5 public void Reload() { // Sync as current properties UpdateImage(); Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionReload); } /// /// Plays the animated GIF. This is also the default playback mode. /// /// 5 public void Play() { // Sync as current properties UpdateImage(); Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionPlay); } /// /// Pauses the animated GIF. /// /// 5 public void Pause() { // Sync as current properties UpdateImage(); Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionPause); } /// /// Stops the animated GIF. /// /// 5 public void Stop() { // Sync as current properties UpdateImage(); Interop.View.DoActionWithEmptyAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionStop); } /// /// Gets or sets the URL of the alpha mask.
/// Optional. ///
/// 6 [EditorBrowsable(EditorBrowsableState.Never)] public string AlphaMaskURL { get { return GetValue(AlphaMaskURLProperty) as string; } set { SetValue(AlphaMaskURLProperty, value); NotifyPropertyChanged(); } } private string InternalAlphaMaskURL { get { string ret = ""; PropertyValue maskUrl = GetCachedImageVisualProperty(ImageVisualProperty.AlphaMaskURL); maskUrl?.Get(out ret); maskUrl?.Dispose(); return ret; } set { 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)) { if (cropToMask == null) { using PropertyValue setCropValue = new PropertyValue(true); UpdateImage(ImageVisualProperty.CropToMask, setCropValue); } } setValue?.Dispose(); } } /// /// Whether to crop image to mask or scale mask to fit image. /// /// 6 public bool CropToMask { get { return (bool)GetValue(CropToMaskProperty); } set { SetValue(CropToMaskProperty, value); NotifyPropertyChanged(); } } private bool InternalCropToMask { get { bool ret = false; PropertyValue cropToMask = GetCachedImageVisualProperty(ImageVisualProperty.CropToMask); cropToMask?.Get(out ret); cropToMask?.Dispose(); return ret; } set { PropertyValue setValue = new PropertyValue(value); UpdateImage(ImageVisualProperty.CropToMask, setValue); setValue?.Dispose(); } } /// /// 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) { case FittingModeType.ShrinkToFit: return VisualFittingModeType.FitKeepAspectRatio; case FittingModeType.ScaleToFill: return VisualFittingModeType.OverFitKeepAspectRatio; case FittingModeType.Center: return VisualFittingModeType.Center; case FittingModeType.Fill: return VisualFittingModeType.Fill; case FittingModeType.FitHeight: return VisualFittingModeType.FitHeight; case FittingModeType.FitWidth: return VisualFittingModeType.FitWidth; default: return VisualFittingModeType.Fill; } } internal FittingModeType ConvertVisualFittingModetoFittingMode(VisualFittingModeType value) { switch (value) { case VisualFittingModeType.FitKeepAspectRatio: return FittingModeType.ShrinkToFit; case VisualFittingModeType.OverFitKeepAspectRatio: return FittingModeType.ScaleToFill; case VisualFittingModeType.Center: return FittingModeType.Center; case VisualFittingModeType.Fill: return FittingModeType.Fill; case VisualFittingModeType.FitHeight: return FittingModeType.FitHeight; case VisualFittingModeType.FitWidth: return FittingModeType.FitWidth; default: return FittingModeType.ShrinkToFit; } } internal override LayoutItem CreateDefaultLayout() { return new ImageLayout(); } /// /// Gets or sets fitting options used when resizing images to fit.
/// If not supplied, the default is FittingModeType.Fill.
/// For normal quad images only.
/// Optional. ///
/// 6 [EditorBrowsable(EditorBrowsableState.Never)] public FittingModeType FittingMode { get { return (FittingModeType)GetValue(FittingModeProperty); } set { SetValue(FittingModeProperty, value); NotifyPropertyChanged(); } } private FittingModeType InternalFittingMode { get { int ret = (int)VisualFittingModeType.Fill; PropertyValue fittingMode = GetCachedImageVisualProperty(Visual.Property.VisualFittingMode); fittingMode?.Get(out ret); fittingMode?.Dispose(); return ConvertVisualFittingModetoFittingMode((VisualFittingModeType)ret); } set { VisualFittingModeType ret = ConvertFittingModetoVisualFittingMode(value); PropertyValue setValue = new PropertyValue((int)ret); UpdateImage(Visual.Property.VisualFittingMode, setValue); setValue?.Dispose(); } } /// /// Gets or sets the desired image width.
/// If not specified, the actual image width is used.
/// For normal quad images only.
/// Optional. ///
/// 6 [EditorBrowsable(EditorBrowsableState.Never)] public int DesiredWidth { get { return (int)GetValue(DesiredWidthProperty); } set { SetValue(DesiredWidthProperty, value); NotifyPropertyChanged(); } } private int InternalDesiredWidth { get { // 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; } set { if (_desired_width != value) { _desired_width = value; PropertyValue setValue = new PropertyValue(value); UpdateImage(ImageVisualProperty.DesiredWidth, setValue, false); setValue?.Dispose(); } } } /// /// Gets or sets the desired image height.
/// If not specified, the actual image height is used.
/// For normal quad images only.
/// Optional. ///
/// 6 [EditorBrowsable(EditorBrowsableState.Never)] public int DesiredHeight { get { return (int)GetValue(DesiredHeightProperty); } set { SetValue(DesiredHeightProperty, value); NotifyPropertyChanged(); } } private int InternalDesiredHeight { get { // 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); desireheight?.Dispose(); return _desired_height; } set { if (_desired_height != value) { _desired_height = value; PropertyValue setValue = new PropertyValue(value); UpdateImage(ImageVisualProperty.DesiredHeight, setValue, false); setValue?.Dispose(); } } } /// /// Gets or sets ReleasePolicy for image.
/// If not supplied, the default is ReleasePolicyType.Detached.
///
[EditorBrowsable(EditorBrowsableState.Never)] public ReleasePolicyType ReleasePolicy { get { return (ReleasePolicyType)GetValue(ReleasePolicyProperty); } set { SetValue(ReleasePolicyProperty, value); NotifyPropertyChanged(); } } private ReleasePolicyType InternalReleasePolicy { get { int ret = (int)ReleasePolicyType.Detached; PropertyValue releasePoli = GetCachedImageVisualProperty(ImageVisualProperty.ReleasePolicy); releasePoli?.Get(out ret); releasePoli?.Dispose(); return (ReleasePolicyType)ret; } set { PropertyValue setValue = new PropertyValue((int)value); UpdateImage(ImageVisualProperty.ReleasePolicy, setValue); setValue?.Dispose(); } } /// /// Gets or sets the wrap mode for the u coordinate.
/// It decides how the texture should be sampled when the u coordinate exceeds the range of 0.0 to 1.0.
/// If not specified, the default is WrapModeType.Default(CLAMP).
/// For normal quad images only.
/// Optional. ///
/// 6 [EditorBrowsable(EditorBrowsableState.Never)] public WrapModeType WrapModeU { get { return (WrapModeType)GetValue(WrapModeUProperty); } set { SetValue(WrapModeUProperty, value); NotifyPropertyChanged(); } } private WrapModeType InternalWrapModeU { get { int ret = (int)WrapModeType.Default; PropertyValue wrapModeU = GetCachedImageVisualProperty(ImageVisualProperty.WrapModeU); wrapModeU?.Get(out ret); wrapModeU?.Dispose(); return (WrapModeType)ret; } set { PropertyValue setValue = new PropertyValue((int)value); UpdateImage(ImageVisualProperty.WrapModeU, setValue); setValue?.Dispose(); } } /// /// Gets or sets the wrap mode for the v coordinate.
/// It decides how the texture should be sampled when the v coordinate exceeds the range of 0.0 to 1.0.
/// The first two elements indicate the top-left position of the area, and the last two elements are the areas of the width and the height respectively.
/// If not specified, the default is WrapModeType.Default(CLAMP).
/// For normal quad images only. /// Optional. ///
/// 6 [EditorBrowsable(EditorBrowsableState.Never)] public WrapModeType WrapModeV { get { return (WrapModeType)GetValue(WrapModeVProperty); } set { SetValue(WrapModeVProperty, value); NotifyPropertyChanged(); } } private WrapModeType InternalWrapModeV { get { int ret = (int)WrapModeType.Default; PropertyValue wrapModeV = GetCachedImageVisualProperty(ImageVisualProperty.WrapModeV); wrapModeV?.Get(out ret); wrapModeV?.Dispose(); return (WrapModeType)ret; } set { PropertyValue setValue = new PropertyValue((int)value); UpdateImage(ImageVisualProperty.WrapModeV, setValue); setValue?.Dispose(); } } /// /// Gets or sets the mode to adjust view size to preserve the aspect ratio of the image resource. /// /// /// 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 { return (bool)GetValue(AdjustViewSizeProperty); } set { SetValue(AdjustViewSizeProperty, value); NotifyPropertyChanged(); } } 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); set { resourceUrlSelector?.Reset(this); if (value == null) return; if (value.HasAll()) SetResourceUrl(value.All); else resourceUrlSelector = new TriggerableSelector(this, value, SetResourceUrl, true); } } /// /// Get attributes, it is abstract function and must be override. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override ViewStyle CreateViewStyle() { return new ImageViewStyle(); } internal void SetImage(string url, Uint16Pair size) { if (url.Contains(".json")) { 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, ConvertResourceUrl(ref url), Uint16Pair.getCPtr(size)); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); _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) { ViewResourceReadySignal ret = new ViewResourceReadySignal(Interop.View.ResourceReadySignal(View.getCPtr(view)), false); if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); return ret; } internal override void ApplyCornerRadius() { base.ApplyCornerRadius(); 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(); if (backgroundExtraData == null) return; // 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() { return (ResourceLoadingStatusType)Interop.View.GetVisualResourceStatus(this.SwigCPtr, Property.IMAGE); } /// /// you can override it to clean-up your own resources. /// /// DisposeTypes /// 3 protected override void Dispose(DisposeTypes type) { if (disposed) { return; } internalPixelArea?.Dispose(); if (type == DisposeTypes.Explicit) { //Called by User //Release your own managed resources here. //You should release all of your own disposable objects here. _border?.Dispose(); _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); } /// This will not be public opened. [EditorBrowsable(EditorBrowsableState.Never)] protected override void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr) { Interop.ImageView.DeleteImageView(swigCPtr); } // Callback for View ResourceReady signal private void OnResourceReady(IntPtr data) { if (!CheckResourceReady()) { return; } ResourceReadyEventArgs e = new ResourceReadyEventArgs(); if (data != null) { e.View = Registry.GetManagedBaseHandleFromNativePtr(data) as View; } if (_resourceReadyEventHandler != null) { _resourceReadyEventHandler(this, e); } } private void SetResourceUrl(string value) { if (_resourceUrl != ConvertResourceUrl(ref value)) { _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(); } } } } private void SetBorder(Rectangle value) { if (value == null) { return; } if (_border != value) { _border = new Rectangle(value); UpdateImage(NpatchImageVisualProperty.Border, new PropertyValue(_border)); } } /// /// Unregist image visual directly. After this operation, we cannot get any properties from Image property. /// private void RemoveImage() { // 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(); // Remove current registed Image. SetProperty(ImageView.Property.IMAGE, emptyValue); // 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(); } /// /// 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) { // Update image property map value as inputed value. if (key != 0) { 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 (cachedImagePropertyMap == null) { cachedImagePropertyMap = new PropertyMap(); } // 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(); } } } /// /// 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) { cachedImagePropertyMap = new PropertyMap(); } // 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))) { // 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(); } } if (backgroundExtraData != null && backgroundExtraData.CornerRadius != null) { using (var cornerRadius = new PropertyValue(backgroundExtraData.CornerRadius)) using (var cornerRadiusPolicy = new PropertyValue((int)backgroundExtraData.CornerRadiusPolicy)) { cachedImagePropertyMap[Visual.Property.CornerRadius] = cornerRadius; cachedImagePropertyMap[Visual.Property.CornerRadiusPolicy] = new PropertyValue((int)(backgroundExtraData.CornerRadiusPolicy)); } } if (backgroundExtraData != null && backgroundExtraData.BorderlineWidth > 0.0f) { using (var borderlineWidth = new PropertyValue(backgroundExtraData.BorderlineWidth)) using (var borderlineColor = new PropertyValue(backgroundExtraData.BorderlineColor)) using (var borderlineOffset = new PropertyValue(backgroundExtraData.BorderlineOffset)) { cachedImagePropertyMap[Visual.Property.BorderlineWidth] = borderlineWidth; cachedImagePropertyMap[Visual.Property.BorderlineColor] = borderlineColor; cachedImagePropertyMap[Visual.Property.BorderlineOffset] = borderlineOffset; } } // 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) { int adjustedDesiredWidth, adjustedDesiredHeight; float aspectOfDesiredSize = (float)_desired_height / (float)_desired_width; float aspectOfImageSize = (float)imageSize.Height / (float)imageSize.Width; if (aspectOfImageSize > aspectOfDesiredSize) { adjustedDesiredWidth = _desired_width; adjustedDesiredHeight = imageSize.Height * _desired_width / imageSize.Width; } else { adjustedDesiredWidth = imageSize.Width * _desired_height / imageSize.Height; adjustedDesiredHeight = _desired_height; } PropertyValue returnWidth = new PropertyValue(adjustedDesiredWidth); cachedImagePropertyMap[ImageVisualProperty.DesiredWidth] = returnWidth; returnWidth?.Dispose(); PropertyValue returnHeight = new PropertyValue(adjustedDesiredHeight); cachedImagePropertyMap[ImageVisualProperty.DesiredHeight] = returnHeight; returnHeight?.Dispose(); PropertyValue scaleToFit = new PropertyValue((int)FittingModeType.ScaleToFill); cachedImagePropertyMap[ImageVisualProperty.FittingMode] = scaleToFit; scaleToFit?.Dispose(); } imageSize?.Dispose(); } } 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); } } } } /// /// 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); if (_resourceLoadedEventHandler != null) { _resourceLoadedEventHandler(this, e); } } /// /// Event arguments of resource ready. /// /// 3 public class ResourceReadyEventArgs : EventArgs { private View _view; /// /// The view whose resource is ready. /// /// 3 public View View { get { return _view; } set { _view = value; } } } internal class ResourceLoadedEventArgs : EventArgs { private ResourceLoadingStatusType status = ResourceLoadingStatusType.Invalid; public ResourceLoadingStatusType Status { get { return status; } set { status = value; } } } internal new class Property { 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 PlaceHolderUrl = Interop.ImageView.PlaceHolderImageGet(); internal static readonly int TransitionEffect = Interop.ImageView.TransitionEffectGet(); } private enum ImageType { /// /// For Normal Image. /// Normal = 0, /// /// For normal image, with synchronous loading and orientation correction property /// Specific = 1, /// /// For nine-patch image /// Npatch = 2, } private void OnBorderChanged(int x, int y, int width, int height) { Border = new Rectangle(x, y, width, height); } private void OnPixelAreaChanged(float x, float y, float z, float w) { PixelArea = new RelativeVector4(x, y, z, w); } private class ImageLayout : LayoutItem { /// /// 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. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool AdjustViewSize { get { return (Owner as ImageView)?.AdjustViewSize ?? false; } set { if (Owner is ImageView imageView) { imageView.AdjustViewSize = value; } } } /// [EditorBrowsable(EditorBrowsableState.Never)] protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) { // To not change the view size by DALi Owner.WidthResizePolicy = ResizePolicyType.Fixed; Owner.HeightResizePolicy = ResizePolicyType.Fixed; float specWidth = widthMeasureSpec.Size.AsDecimal(); float specHeight = heightMeasureSpec.Size.AsDecimal(); float naturalWidth = Owner.NaturalSize.Width; float naturalHeight = Owner.NaturalSize.Height; float minWidth = Owner.MinimumSize.Width; float maxWidth = Owner.MaximumSize.Width; float minHeight = Owner.MinimumSize.Height; float maxHeight = Owner.MaximumSize.Height; float aspectRatio = (naturalWidth > 0) ? (naturalHeight / naturalWidth) : 0; // Assume that the new width and height are given from the view's suggested size by default. float newWidth = Math.Min(Math.Max(naturalWidth, minWidth), (maxWidth < 0 ? Int32.MaxValue : maxWidth)); float newHeight = Math.Min(Math.Max(naturalHeight, minHeight), (maxHeight < 0 ? Int32.MaxValue : maxHeight)); // The width and height measure specs are going to be used to set measured size. // Mark that the measure specs are changed by default to update measure specs later. bool widthSpecChanged = true; bool heightSpecChanged = true; if (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) { newWidth = specWidth; widthSpecChanged = false; if (heightMeasureSpec.Mode != MeasureSpecification.ModeType.Exactly) { if ((AdjustViewSize) && (aspectRatio > 0)) { newHeight = newWidth * aspectRatio; } } } if (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) { newHeight = specHeight; heightSpecChanged = false; if (widthMeasureSpec.Mode != MeasureSpecification.ModeType.Exactly) { if ((AdjustViewSize) && (aspectRatio > 0)) { newWidth = newHeight / aspectRatio; } } } if (widthSpecChanged) { widthMeasureSpec = new MeasureSpecification(new LayoutLength(newWidth), MeasureSpecification.ModeType.Exactly); } if (heightSpecChanged) { heightMeasureSpec = new MeasureSpecification(new LayoutLength(newHeight), MeasureSpecification.ModeType.Exactly); } MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK; MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK; SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(newWidth), widthMeasureSpec, childWidthState), ResolveSizeAndState(new LayoutLength(newHeight), heightMeasureSpec, childHeightState)); } } } }