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;
}
}