diff --git a/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj b/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj index 206ed3229951..cea07ff91028 100644 --- a/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj +++ b/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj @@ -67,6 +67,7 @@ + diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue21886.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue21886.cs new file mode 100644 index 000000000000..7a1a89476415 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue21886.cs @@ -0,0 +1,88 @@ +using System.Reflection; +using Microsoft.Maui.Graphics.Platform; +using IImage = Microsoft.Maui.Graphics.IImage; + +namespace Controls.TestCases.HostApp.Issues; + +[Issue(IssueTracker.Github, 21886, "The original image remains undisposed even after setting disposeOriginal to true in the Resize and Downsize methods", PlatformAffected.Android | PlatformAffected.iOS)] +public class Issue21886 : ContentPage +{ + Label _originalImageStatusLabel; + + public Issue21886() + { + _originalImageStatusLabel = new Label + { + AutomationId = "OriginalImageStatusLabel", + Text = "Status of Original Image Disposal" + }; + + VerticalStackLayout stackLayout = new VerticalStackLayout + { + Children = + { + CreateButton("Resize", OnResize), + CreateButton("DownSize", OnDownSize), + _originalImageStatusLabel, + } + }; + + Content = new ScrollView { Content = stackLayout }; + } + + Button CreateButton(string text, EventHandler handler) + { + Button button = new Button + { + AutomationId = $"Issue21886{text}Btn", + Text = text, + HorizontalOptions = LayoutOptions.Fill + }; + + button.Clicked += handler; + return button; + } + + async Task LoadImageAsync() + { + var assembly = GetType().GetTypeInfo().Assembly; + using var stream = assembly.GetManifestResourceStream("Controls.TestCases.HostApp.Resources.Images.royals.png"); + return await Task.FromResult(PlatformImage.FromStream(stream)); + } + + async void OnResize(object sender, EventArgs e) + { + var image = await LoadImageAsync(); + var res = image.Resize(10, 10, ResizeMode.Fit, true); + + UpdateStatusLabels(res, image, "Resize"); + } + + async void OnDownSize(object sender, EventArgs e) + { + var image = await LoadImageAsync(); + var res = image.Downsize(10, 10, true); + + UpdateStatusLabels(res, image, "Downsize"); + } + + void UpdateStatusLabels(IImage resultImage, IImage originalImage, string operation) + { + _originalImageStatusLabel.Text = TryAccessImage(originalImage) + ? "Success" + : originalImage.Width == 0 && originalImage.Height == 0 ? "Success" : "Failure"; + } + + bool TryAccessImage(IImage image) + { + try + { + var _ = image.Width; + return false; + } + catch (ObjectDisposedException) + { + return true; + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Images/royals.png b/src/Controls/tests/TestCases.HostApp/Resources/Images/royals.png new file mode 100644 index 000000000000..c2c29a644799 Binary files /dev/null and b/src/Controls/tests/TestCases.HostApp/Resources/Images/royals.png differ diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21886.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21886.cs new file mode 100644 index 000000000000..f4f80554b061 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21886.cs @@ -0,0 +1,32 @@ +#if TEST_FAILS_ON_WINDOWS // Issue Link - https://github.com/dotnet/maui/issues/16767 +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue21886 : _IssuesUITest +{ + public Issue21886(TestDevice device) : base(device) + { + } + + public override string Issue => "The original image remains undisposed even after setting disposeOriginal to true in the Resize and Downsize methods"; + + [Test] + [Category(UITestCategories.GraphicsView)] + public void VerifyOriginalImageBeingDisposed() + { + App.WaitForElement("OriginalImageStatusLabel"); + App.Tap("Issue21886ResizeBtn"); + + var resizeLabelText = App.FindElement("OriginalImageStatusLabel").GetText(); + Assert.That(resizeLabelText, Is.EqualTo("Success")); + + App.Tap("Issue21886DownSizeBtn"); + + var downsizeLabelText = App.FindElement("OriginalImageStatusLabel").GetText(); + Assert.That(downsizeLabelText, Is.EqualTo("Success")); + } +} +#endif \ No newline at end of file diff --git a/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs b/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs index f353d1a44e19..a08072db46d2 100644 --- a/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs +++ b/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs @@ -15,9 +15,9 @@ public PlatformImage(Bitmap bitmap) _bitmap = bitmap; } - public float Width => _bitmap.Width; + public float Width => _bitmap?.Width ?? 0; - public float Height => _bitmap.Height; + public float Height => _bitmap?.Height ?? 0; public IImage Downsize(float maxWidthOrHeight, bool disposeOriginal = false) { @@ -83,6 +83,13 @@ public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMo } context.Canvas.DrawImage(this, x, y, w, h); + + if (disposeOriginal) + { + _bitmap.Recycle(); + _bitmap.Dispose(); + } + return context.Image; } } diff --git a/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs b/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs index 9fe381776361..5df006559f7e 100644 --- a/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs +++ b/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs @@ -84,6 +84,12 @@ public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMo } context.Canvas.DrawImage(this, x, y, w, h); + + if (disposeOriginal) + { + _image.Dispose(); + } + return context.Image; } }