Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue32869.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.ComponentModel;

namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 32869, "Image control crashes on Android when image width exceeds height", PlatformAffected.Android)]
public class Issue32869 : ContentPage
{
Image _testImage;
public Issue32869()
{
Title = "Wide Image Test";
Padding = new Thickness(24);
_testImage = new Image
{
AutomationId = "TestImage",
};
Content = _testImage;
}

protected override async void OnAppearing()
{
base.OnAppearing();
try
{
await LoadWideImageAsync();
}
catch (Exception ex)
{
// Optionally display error to user or log
_testImage.Source = null;
await DisplayAlert("Error", $"Failed to load image: {ex.Message}", "OK");
}
}

private async Task LoadWideImageAsync()
{
// Load the wide image from embedded resources
await using var stream = await FileSystem.OpenAppPackageFileAsync("Issue32869.png");
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var imageBytes = ms.ToArray();
// Write to local storage
var localPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "test_wide_image.png");
await using var fileStream = new FileStream(localPath, FileMode.Create);
await fileStream.WriteAsync(imageBytes);
// Load the image
_testImage.Source = localPath;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue32869 : _IssuesUITest
{
public Issue32869(TestDevice testDevice) : base(testDevice)
{
}

public override string Issue => "Image control crashes on Android when image width exceeds height";

[Test]
[Category(UITestCategories.Image)]
public void Issue32869_Image()
{
App.WaitForElement("TestImage");
VerifyScreenshot();
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ static PorterDuff.Mode getPorterMode(int mode) {
}

private static void prepare(RequestBuilder<Drawable> builder, MauiTarget target, boolean cachingEnabled, ImageLoaderCallback callback) {
prepare(builder, target, cachingEnabled, callback, false);
}

private static void prepare(RequestBuilder<Drawable> builder, MauiTarget target, boolean cachingEnabled, ImageLoaderCallback callback, boolean constrainSize) {
// A special value to work around https://github.com/dotnet/maui/issues/6783 where targets
// are actually re-used if all the variables are the same.
// Adding this "error image" that will always load a null image makes each request unique,
Expand All @@ -310,6 +314,13 @@ private static void prepare(RequestBuilder<Drawable> builder, MauiTarget target,
.skipMemoryCache(true);
}

// Constrain bitmap size to prevent excessive memory allocation
// See https://github.com/dotnet/maui/issues/32869
if (constrainSize) {
builder = builder
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
}

target.load(builder);
}

Expand All @@ -318,20 +329,28 @@ public static String getGlyphHex(String glyph) {
}

private static void loadInto(RequestBuilder<Drawable> builder, ImageView imageView, boolean cachingEnabled, ImageLoaderCallback callback, Object model) {
loadInto(builder, imageView, cachingEnabled, callback, model, false);
}

private static void loadInto(RequestBuilder<Drawable> builder, ImageView imageView, boolean cachingEnabled, ImageLoaderCallback callback, Object model, boolean constrainSize) {
MauiCustomViewTarget target = new MauiCustomViewTarget(imageView, callback, model);
prepare(builder, target, cachingEnabled, callback);
prepare(builder, target, cachingEnabled, callback, constrainSize);
}

private static void load(RequestBuilder<Drawable> builder, Context context, boolean cachingEnabled, ImageLoaderCallback callback, Object model) {
load(builder, context, cachingEnabled, callback, model, false);
}

private static void load(RequestBuilder<Drawable> builder, Context context, boolean cachingEnabled, ImageLoaderCallback callback, Object model, boolean constrainSize) {
MauiCustomTarget target = new MauiCustomTarget(context, callback, model);
prepare(builder, target, cachingEnabled, callback);
prepare(builder, target, cachingEnabled, callback, constrainSize);
}

public static void loadImageFromFile(ImageView imageView, String file, ImageLoaderCallback callback) {
RequestBuilder<Drawable> builder = Glide
.with(imageView)
.load(file);
loadInto(builder, imageView, true, callback, file);
loadInto(builder, imageView, true, callback, file, true);
}

public static void loadImageFromUri(ImageView imageView, String uri, boolean cachingEnabled, ImageLoaderCallback callback) {
Expand All @@ -343,14 +362,14 @@ public static void loadImageFromUri(ImageView imageView, String uri, boolean cac
RequestBuilder<Drawable> builder = Glide
.with(imageView)
.load(androidUri);
loadInto(builder, imageView, cachingEnabled, callback, androidUri);
loadInto(builder, imageView, cachingEnabled, callback, androidUri, true);
}

public static void loadImageFromStream(ImageView imageView, InputStream inputStream, ImageLoaderCallback callback) {
RequestBuilder<Drawable> builder = Glide
.with(imageView)
.load(inputStream);
loadInto(builder, imageView, false, callback, inputStream);
loadInto(builder, imageView, false, callback, inputStream, true);
}

public static void loadImageFromFont(ImageView imageView, @ColorInt int color, String glyph, Typeface typeface, float textSize, ImageLoaderCallback callback) {
Expand All @@ -366,7 +385,7 @@ public static void loadImageFromFile(Context context, String file, ImageLoaderCa
RequestBuilder<Drawable> builder = Glide
.with(context)
.load(file);
load(builder, context, true, callback, file);
load(builder, context, true, callback, file, true);
}

public static void loadImageFromUri(Context context, String uri, boolean cachingEnabled, ImageLoaderCallback callback) {
Expand All @@ -378,14 +397,14 @@ public static void loadImageFromUri(Context context, String uri, boolean caching
RequestBuilder<Drawable> builder = Glide
.with(context)
.load(androidUri);
load(builder, context, cachingEnabled, callback, androidUri);
load(builder, context, cachingEnabled, callback, androidUri, true);
}

public static void loadImageFromStream(Context context, InputStream inputStream, ImageLoaderCallback callback) {
RequestBuilder<Drawable> builder = Glide
.with(context)
.load(inputStream);
load(builder, context, false, callback, inputStream);
load(builder, context, false, callback, inputStream, true);
}

public static void loadImageFromFont(Context context, @ColorInt int color, String glyph, Typeface typeface, float textSize, ImageLoaderCallback callback) {
Expand Down
Loading