diff --git a/src/Controls/src/Core/Button/Button.iOS.cs b/src/Controls/src/Core/Button/Button.iOS.cs index 863c2b0d0ff6..c710d5a2a8de 100644 --- a/src/Controls/src/Core/Button/Button.iOS.cs +++ b/src/Controls/src/Core/Button/Button.iOS.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Graphics.Platform; using Microsoft.Maui.Handlers; using Microsoft.Maui.Layouts; using UIKit; @@ -393,7 +394,7 @@ bool ResizeImageIfNecessary(UIButton platformButton, Button button, UIImage imag // if the image is too large then we will size it smaller if (currentImageHeight - availableHeight > buffer || currentImageWidth - availableWidth > buffer) { - image = ResizeImageSource(image, availableWidth, availableHeight, _originalImageSize); + image = image.ResizeImageSource(availableWidth, availableHeight, _originalImageSize); } // if the image is already sized down but now has more space, we will size it up no more than the original image size else if (availableHeight - additionalVerticalSpace - currentImageHeight > buffer @@ -401,7 +402,7 @@ bool ResizeImageIfNecessary(UIButton platformButton, Button button, UIImage imag && currentImageHeight != _originalImageSize.Height && currentImageWidth != _originalImageSize.Width) { - image = ResizeImageSource(image, (nfloat)widthConstraint - additionalHorizontalSpace, (nfloat)heightConstraint - additionalVerticalSpace, _originalImageSize, true); + image = image.ResizeImageSource((nfloat)widthConstraint - additionalHorizontalSpace, (nfloat)heightConstraint - additionalVerticalSpace, _originalImageSize, true); } else { @@ -422,33 +423,6 @@ bool ResizeImageIfNecessary(UIButton platformButton, Button button, UIImage imag return false; } - /// - /// Resize the image to fit within the constraints. - /// - /// - /// - /// - /// - /// - /// - static UIImage ResizeImageSource(UIImage sourceImage, nfloat maxWidth, nfloat maxHeight, CGSize originalImageSize, bool shouldScaleUp = false) - { - if (sourceImage is null || sourceImage.CGImage is null) - return null; - - maxWidth = (nfloat)Math.Min(maxWidth, originalImageSize.Width); - maxHeight = (nfloat)Math.Min(maxHeight, originalImageSize.Height); - - var sourceSize = sourceImage.Size; - - float maxResizeFactor = (float)Math.Min(maxWidth / sourceSize.Width, maxHeight / sourceSize.Height); - - if (maxResizeFactor > 1 && !shouldScaleUp) - return sourceImage; - - return UIImage.FromImage(sourceImage.CGImage, sourceImage.CurrentScale / maxResizeFactor, sourceImage.Orientation); - } - public static void MapText(ButtonHandler handler, Button button) => MapText((IButtonHandler)handler, button); diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png index 9c7bdee2fb6d..d0fc26d2309c 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderThumbImageShouldBeScaled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderThumbImageShouldBeScaled.png new file mode 100644 index 000000000000..00ddb986bfde Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderThumbImageShouldBeScaled.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png index 12f2706e2e25..5be2e8b274ce 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png index d2304f47a58c..408298799ccc 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png index 76afb10e9b2b..f8c3d4877211 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png index 9c716cf138d7..c364531e72fc 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue13258.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue13258.cs new file mode 100644 index 000000000000..132f18135e1b --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue13258.cs @@ -0,0 +1,51 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 13258, "MAUI Slider thumb image is big on android", PlatformAffected.Android | PlatformAffected.iOS | PlatformAffected.macOS)] +public class Issue13258 : TestContentPage +{ + protected override void Init() + { + StackLayout rootLayout = new StackLayout() { Spacing = 10, Padding = 10 }; + + Label slider1DescriptionLabel = CreateLabel("Slider with Thumb Image"); + Slider slider1 = CreateSlider("avatar.png"); + + Label slider2DescriptionLabel = CreateLabel("Thumb Image will be set to coffee.png at run time"); + Slider slider2 = CreateSlider(); + + Label slider3DescriptionLabel = CreateLabel("Thumb Image will be set to null at run time"); + Slider slider3 = CreateSlider("shopping_cart.png"); + + Button button = new Button() { Text = "Change Thumb Image", AutomationId = "ToggleImageBtn" }; + button.Clicked += (s, e) => ToggleThumbImages(slider2, slider3); + + rootLayout.Children.Add(slider1DescriptionLabel); + rootLayout.Children.Add(slider1); + + rootLayout.Children.Add(slider2DescriptionLabel); + rootLayout.Children.Add(slider2); + + rootLayout.Children.Add(slider3DescriptionLabel); + rootLayout.Children.Add(slider3); + + rootLayout.Children.Add(button); + + Content = rootLayout; + } + + Label CreateLabel(string text) + { + return new Label { Text = text }; + } + + Slider CreateSlider(string thumbImageSource = null) + { + return new Slider { ThumbImageSource = thumbImageSource }; + } + + private void ToggleThumbImages(Slider slider2, Slider slider3) + { + slider2.ThumbImageSource = "coffee.png"; + slider3.ThumbImageSource = null; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SliderShouldChangeThumbImageAndResetIt.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SliderShouldChangeThumbImageAndResetIt.png index c47213f9e1ac..b0ac63559e43 100644 Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SliderShouldChangeThumbImageAndResetIt.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SliderShouldChangeThumbImageAndResetIt.png differ diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SliderThumbImageShouldBeScaled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SliderThumbImageShouldBeScaled.png new file mode 100644 index 000000000000..d24ce93240f0 Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SliderThumbImageShouldBeScaled.png differ diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_ChangeThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_ChangeThumbImageSource_VerifyVisualState.png index 01e7e9edbba5..1fd61495bd7b 100644 Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_ChangeThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_ChangeThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png index b27cbcca4498..00b9af8d69f8 100644 Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetValueAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetValueAndThumbImageSource_VerifyVisualState.png index 1fb74ae1b4fe..d62ca6ab9d6f 100644 Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetValueAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetValueAndThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13258.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13258.cs new file mode 100644 index 000000000000..fd5e90176234 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13258.cs @@ -0,0 +1,25 @@ +#if TEST_FAILS_ON_WINDOWS //Issue link - https://github.com/dotnet/maui/issues/29125 +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue13258 : _IssuesUITest +{ + public Issue13258(TestDevice testDevice) : base(testDevice) + { + } + + public override string Issue => "MAUI Slider thumb image is big on android"; + + [Test] + [Category(UITestCategories.Slider)] + public void SliderThumbImageShouldBeScaled() + { + App.WaitForElement("ToggleImageBtn"); + App.Tap("ToggleImageBtn"); + VerifyScreenshot(); + } +} +#endif \ No newline at end of file diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SliderShouldChangeThumbImageAndResetIt.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SliderShouldChangeThumbImageAndResetIt.png index cc02b443b479..77d6eead4df9 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SliderShouldChangeThumbImageAndResetIt.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SliderShouldChangeThumbImageAndResetIt.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SliderThumbImageShouldBeScaled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SliderThumbImageShouldBeScaled.png new file mode 100644 index 000000000000..9e6cd564323e Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SliderThumbImageShouldBeScaled.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_ChangeThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_ChangeThumbImageSource_VerifyVisualState.png index 5c8688711ae9..3161b32d739a 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_ChangeThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_ChangeThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png index a269003c5660..2d7998f789ab 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetValueAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetValueAndThumbImageSource_VerifyVisualState.png index 8f3ecb8601ad..5035c06244e1 100644 Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetValueAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetValueAndThumbImageSource_VerifyVisualState.png differ diff --git a/src/Core/src/Platform/Android/SliderExtensions.cs b/src/Core/src/Platform/Android/SliderExtensions.cs index 2dc0707f636a..3dc521c82f59 100644 --- a/src/Core/src/Platform/Android/SliderExtensions.cs +++ b/src/Core/src/Platform/Android/SliderExtensions.cs @@ -1,8 +1,11 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Android.Content.Res; using Android.Graphics; +using Android.Graphics.Drawables; using Android.Util; using Android.Widget; +using Microsoft.Maui.Graphics.Platform; namespace Microsoft.Maui.Platform { @@ -10,6 +13,10 @@ public static class SliderExtensions { public const double PlatformMaxValue = int.MaxValue; + //Material 2 design spec - https://m2.material.io/components/sliders/android#discrete-slider + //Additional info - https://github.com/material-components/material-components-android/blob/60b0325b39741784fca4d7aba079b65453bc7c66/lib/java/com/google/android/material/slider/res/values/dimens.xml#L27 + const int TARGET_SIZE = 20; // 10 radius * 2 + public static void UpdateMinimum(this SeekBar seekBar, ISlider slider) => UpdateValue(seekBar, slider); public static void UpdateMaximum(this SeekBar seekBar, ISlider slider) => UpdateValue(seekBar, slider); @@ -25,7 +32,7 @@ public static void UpdateValue(this SeekBar seekBar, ISlider slider) public static void UpdateMinimumTrackColor(this SeekBar seekBar, ISlider slider) { - if (slider.MinimumTrackColor != null) + if (slider.MinimumTrackColor is not null) { seekBar.ProgressTintList = ColorStateList.ValueOf(slider.MinimumTrackColor.ToPlatform()); seekBar.ProgressTintMode = PorterDuff.Mode.SrcIn; @@ -34,7 +41,7 @@ public static void UpdateMinimumTrackColor(this SeekBar seekBar, ISlider slider) public static void UpdateMaximumTrackColor(this SeekBar seekBar, ISlider slider) { - if (slider.MaximumTrackColor != null) + if (slider.MaximumTrackColor is not null) { seekBar.ProgressBackgroundTintList = ColorStateList.ValueOf(slider.MaximumTrackColor.ToPlatform()); seekBar.ProgressBackgroundTintMode = PorterDuff.Mode.SrcIn; @@ -47,37 +54,81 @@ public static void UpdateThumbColor(this SeekBar seekBar, ISlider slider) => public static async Task UpdateThumbImageSourceAsync(this SeekBar seekBar, ISlider slider, IImageSourceServiceProvider provider) { var context = seekBar.Context; - - if (context == null) + if (context is null) + { return; + } var thumbImageSource = slider.ThumbImageSource; - - if (thumbImageSource != null) + if (thumbImageSource is not null) { var service = provider.GetRequiredImageSourceService(thumbImageSource); var result = await service.GetDrawableAsync(thumbImageSource, context); - var thumbDrawable = result?.Value; - if (seekBar.IsAlive() && thumbDrawable != null) - seekBar.SetThumb(thumbDrawable); + if (seekBar.IsAlive()) + { + if (thumbDrawable is not null) + { + SetThumbDrawable(seekBar, context, thumbDrawable); + } + else + { + SetDefaultThumb(seekBar, slider, context); + } + } } else { - seekBar.SetThumb(context.GetDrawable(Resource.Drawable.abc_seekbar_thumb_material)); - if (slider.ThumbColor is null && context.Theme is not null) + SetDefaultThumb(seekBar, slider, context); + } + } + + static void SetThumbDrawable(SeekBar seekBar, Android.Content.Context context, Drawable thumbDrawable) + { + // Check if we're setting the same drawable to avoid unnecessary work + if (ReferenceEquals(seekBar.Thumb, thumbDrawable)) + { + return; + } + + int thumbSize = (int)context.ToPixels(TARGET_SIZE); + + if (thumbSize <= 0) + { + return; + } + + + using (Bitmap bitmap = Bitmap.CreateBitmap(thumbSize, thumbSize, Bitmap.Config.Argb8888!)) + using (Canvas canvas = new Canvas(bitmap)) + { + thumbDrawable.SetBounds(0, 0, thumbSize, thumbSize); + thumbDrawable.Draw(canvas); + + using (BitmapDrawable finalDrawable = new BitmapDrawable(context.Resources, bitmap)) { - using var value = new TypedValue(); - context.Theme.ResolveAttribute(Android.Resource.Attribute.ColorAccent, value, true); - var color = new Color(value.Data); - seekBar.Thumb?.SetColorFilter(color, FilterMode.SrcIn); + seekBar.SetThumb(finalDrawable); } - else + } + } + + static void SetDefaultThumb(SeekBar seekBar, ISlider slider, Android.Content.Context context) + { + seekBar.SetThumb(context.GetDrawable(Resource.Drawable.abc_seekbar_thumb_material)); + + if (slider.ThumbColor is null && context.Theme is not null) + { + using var value = new TypedValue(); + if (context.Theme.ResolveAttribute(Android.Resource.Attribute.ColorAccent, value, true)) { - seekBar.UpdateThumbColor(slider); + seekBar.Thumb?.SetColorFilter(new Color(value.Data), FilterMode.SrcIn); } } + else + { + seekBar.UpdateThumbColor(slider); + } } } } \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/SliderExtensions.cs b/src/Core/src/Platform/iOS/SliderExtensions.cs index 5555cf9ac5ea..14c2b806fb91 100644 --- a/src/Core/src/Platform/iOS/SliderExtensions.cs +++ b/src/Core/src/Platform/iOS/SliderExtensions.cs @@ -1,5 +1,7 @@ using System.Threading.Tasks; +using CoreGraphics; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Maui.Graphics.Platform; using ObjCRuntime; using UIKit; @@ -20,37 +22,56 @@ public static void UpdateMaximum(this UISlider uiSlider, ISlider slider) public static void UpdateValue(this UISlider uiSlider, ISlider slider) { if ((float)slider.Value != uiSlider.Value) + { uiSlider.Value = (float)slider.Value; + } } public static void UpdateMinimumTrackColor(this UISlider uiSlider, ISlider slider) { - if (slider.MinimumTrackColor != null) + if (slider.MinimumTrackColor is not null) + { uiSlider.MinimumTrackTintColor = slider.MinimumTrackColor.ToPlatform(); + } } public static void UpdateMaximumTrackColor(this UISlider uiSlider, ISlider slider) { - if (slider.MaximumTrackColor != null) + if (slider.MaximumTrackColor is not null) + { uiSlider.MaximumTrackTintColor = slider.MaximumTrackColor.ToPlatform(); + } } public static void UpdateThumbColor(this UISlider uiSlider, ISlider slider) { - if (slider.ThumbColor != null) + if (slider.ThumbColor is not null) + { uiSlider.ThumbTintColor = slider.ThumbColor.ToPlatform(); + } } public static async Task UpdateThumbImageSourceAsync(this UISlider uiSlider, ISlider slider, IImageSourceServiceProvider provider) { var thumbImageSource = slider.ThumbImageSource; - - if (thumbImageSource != null) + if (thumbImageSource is not null) { var service = provider.GetRequiredImageSourceService(thumbImageSource); var scale = uiSlider.GetDisplayDensity(); var result = await service.GetImageAsync(thumbImageSource, scale); - var thumbImage = result?.Value; + var thumbImageSize = result?.Value.Size ?? CGSize.Empty; + var defaultThumbSize = CalculateDefaultThumbSize(uiSlider); + + UIImage? thumbImage; + if (thumbImageSize.IsEmpty) + { + thumbImage = result?.Value; + } + else + { + // Resize the image if the size is valid + thumbImage = result?.Value?.ResizeImageSource(defaultThumbSize.Width, defaultThumbSize.Height, thumbImageSize); + } uiSlider.SetThumbImage(thumbImage, UIControlState.Normal); } @@ -60,5 +81,12 @@ public static async Task UpdateThumbImageSourceAsync(this UISlider uiSlider, ISl uiSlider.UpdateThumbColor(slider); } } + + static CGSize CalculateDefaultThumbSize(UISlider uiSlider) + { + var trackRect = uiSlider.TrackRectForBounds(uiSlider.Bounds); + var thumbRect = uiSlider.ThumbRectForBounds(uiSlider.Bounds, trackRect, 0); + return thumbRect.Size; + } } } \ No newline at end of file diff --git a/src/Graphics/src/Graphics/Platforms/iOS/UIImageExtensions.cs b/src/Graphics/src/Graphics/Platforms/iOS/UIImageExtensions.cs index ab9d9268e597..59aec8cdc1be 100644 --- a/src/Graphics/src/Graphics/Platforms/iOS/UIImageExtensions.cs +++ b/src/Graphics/src/Graphics/Platforms/iOS/UIImageExtensions.cs @@ -1,3 +1,4 @@ +using System; using CoreGraphics; using UIKit; @@ -70,5 +71,27 @@ public static UIImage NormalizeOrientation(this UIImage target, bool disposeOrig return image; } + + internal static UIImage ResizeImageSource(this UIImage sourceImage, nfloat maxWidth, nfloat maxHeight, CGSize originalImageSize, bool shouldScaleUp = false) + { + if (sourceImage is null || sourceImage.CGImage is null) + { + return null; + } + + maxWidth = (nfloat)Math.Min(maxWidth, originalImageSize.Width); + maxHeight = (nfloat)Math.Min(maxHeight, originalImageSize.Height); + + var sourceSize = sourceImage.Size; + + float maxResizeFactor = (float)Math.Min(maxWidth / sourceSize.Width, maxHeight / sourceSize.Height); + + if (maxResizeFactor > 1 && !shouldScaleUp) + { + return sourceImage; + } + + return UIImage.FromImage(sourceImage.CGImage, sourceImage.CurrentScale / maxResizeFactor, sourceImage.Orientation); + } } } \ No newline at end of file