KEMBAR78
[android] improve performance of `ImageHandler.PlatformArrange()` by jonathanpeppers · Pull Request #23665 · dotnet/maui · GitHub
Skip to content

Conversation

jonathanpeppers
Copy link
Member

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt the slowest on Android.

One thing I noticed while scrolling:

98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, PlatformArrange() is called a lot for every <Image/>:

if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
{
    var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
    var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
    PlatformView.ClipBounds = clipRect;
}

ImageView.ScaleType is a class, and so and some bookkeeping is done to lookup the same C# instance for a Java object. We can make this a bit better by writing a new Java method:

public static boolean isImageViewCenterCrop(@NonNull ImageView imageView) {
    return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
}

Next, let's make a PlatformView.ToPixels() extension method that can avoid calling View.Context for the same reason.

Lastly, we can make a PlatformInterop.SetClipBounds() method to avoid creating a Android.Graphics.Rect object in C#.

With these changes, I can only see the topmost PlatformArrange() method now:

2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI <Image/> on Android. I also "banned" GetScaleType() in eng/BannedSymbols.txt.

@jonathanpeppers jonathanpeppers added platform/android perf/general The issue affects performance (runtime speed, memory usage, startup time, etc.) (sub: perf) labels Jul 17, 2024
@jonathanpeppers

This comment was marked as outdated.

@Eilon Eilon added the area-controls-image Image control label Jul 19, 2024
@jonathanpeppers

This comment was marked as outdated.

@jonathanpeppers jonathanpeppers force-pushed the ImageView.GetScaleType branch from 8dcd951 to d52ca17 Compare July 31, 2024 16:28
@bcaceiro
Copy link

Is this going into next SR? Seems like a great adition!

@bcaceiro
Copy link

Any news on this?

@bcaceiro
Copy link

Sorry to bump this, but feels like this is an important MR to improve performance overall in a basic element like Image in all applications

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt
the slowest on Android.

One thing I noticed while scrolling:

    98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
    67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, `PlatformArrange()` is called a lot for every `<Image/>`:

    if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
    {
        var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
        var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
        PlatformView.ClipBounds = clipRect;
    }

`ImageView.ScaleType` is a class, and so and some bookkeeping is done
to lookup *the same* C# instance for a Java object. We can make this a
bit better by writing a new Java method:

    public static boolean isImageViewCenterCrop(@nonnull ImageView imageView) {
        return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
    }

Next, let's make a `PlatformView.ToPixels()` extension method that can
avoid calling `View.Context` for the same reason.

Lastly, we can make a `PlatformInterop.SetClipBounds()` method to avoid
creating a `Android.Graphics.Rect` object in C#.

With these changes, I can only see the topmost `PlatformArrange()`
method now:

    2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI `<Image/>`
on Android. I also "banned" `GetScaleType()` in `eng/BannedSymbols.txt`.
@jonathanpeppers jonathanpeppers force-pushed the ImageView.GetScaleType branch from d52ca17 to 0ed8055 Compare August 7, 2025 13:22
@jonathanpeppers
Copy link
Member Author

@bcaceiro I originally held off on this because it could be fixed here:

We'll try to merge it now, as those changes didn't land.

@jonathanpeppers jonathanpeppers marked this pull request as ready for review August 7, 2025 15:59
@Copilot Copilot AI review requested due to automatic review settings August 7, 2025 15:59
@jonathanpeppers jonathanpeppers requested a review from a team as a code owner August 7, 2025 15:59
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR improves the performance of ImageHandler.PlatformArrange() on Android by optimizing several expensive operations during image layout. The changes focus on reducing object allocations and method call overhead during frequent layout operations.

Key changes:

  • Replaces expensive GetScaleType() calls with a native Java method that avoids C# object creation
  • Introduces extension methods to avoid unnecessary View.Context property access
  • Adds native Java methods to set clip bounds without creating Android.Graphics.Rect objects in C#

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Core/src/Handlers/Image/ImageHandler.Android.cs Updates PlatformArrange() to use new optimized methods for scale type checking and clip bounds setting
src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformInterop.java Adds native Java methods isImageViewCenterCrop() and setClipBounds() to avoid object allocation overhead
src/Core/src/Platform/Android/ContextExtensions.cs Adds ToPixels() extension method for View to avoid accessing Context property
src/Compatibility/Core/src/Android/FastRenderers/ImageElementManager.cs Updates compatibility layer to use new optimized scale type checking method
eng/BannedSymbols.txt Bans GetScaleType() method to prevent future performance regressions
src/Core/tests/DeviceTests/Core.DeviceTests.csproj Adds IsTestProject property for proper test project identification
src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj Adds IsTestProject property for proper test project identification

@PureWeen PureWeen added this to the .NET 9 SR11 milestone Aug 7, 2025
@PureWeen PureWeen moved this from Todo to Ready To Review in MAUI SDK Ongoing Aug 7, 2025
@github-project-automation github-project-automation bot moved this from Ready To Review to Approved in MAUI SDK Ongoing Aug 7, 2025
@PureWeen PureWeen changed the base branch from main to inflight/current August 7, 2025 18:22
@PureWeen PureWeen merged commit 9b2f71f into dotnet:inflight/current Aug 7, 2025
127 of 130 checks passed
@github-project-automation github-project-automation bot moved this from Approved to Done in MAUI SDK Ongoing Aug 7, 2025
PureWeen pushed a commit that referenced this pull request Aug 8, 2025
…3665)

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt
the slowest on Android.

One thing I noticed while scrolling:

    98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
    67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, `PlatformArrange()` is called a lot for every `<Image/>`:

    if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
    {
        var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
        var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
        PlatformView.ClipBounds = clipRect;
    }

`ImageView.ScaleType` is a class, and so and some bookkeeping is done
to lookup *the same* C# instance for a Java object. We can make this a
bit better by writing a new Java method:

    public static boolean isImageViewCenterCrop(@nonnull ImageView imageView) {
        return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
    }

Next, let's make a `PlatformView.ToPixels()` extension method that can
avoid calling `View.Context` for the same reason.

Lastly, we can make a `PlatformInterop.SetClipBounds()` method to avoid
creating a `Android.Graphics.Rect` object in C#.

With these changes, I can only see the topmost `PlatformArrange()`
method now:

    2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI `<Image/>`
on Android. I also "banned" `GetScaleType()` in `eng/BannedSymbols.txt`.
github-actions bot pushed a commit that referenced this pull request Aug 11, 2025
…3665)

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt
the slowest on Android.

One thing I noticed while scrolling:

    98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
    67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, `PlatformArrange()` is called a lot for every `<Image/>`:

    if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
    {
        var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
        var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
        PlatformView.ClipBounds = clipRect;
    }

`ImageView.ScaleType` is a class, and so and some bookkeeping is done
to lookup *the same* C# instance for a Java object. We can make this a
bit better by writing a new Java method:

    public static boolean isImageViewCenterCrop(@nonnull ImageView imageView) {
        return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
    }

Next, let's make a `PlatformView.ToPixels()` extension method that can
avoid calling `View.Context` for the same reason.

Lastly, we can make a `PlatformInterop.SetClipBounds()` method to avoid
creating a `Android.Graphics.Rect` object in C#.

With these changes, I can only see the topmost `PlatformArrange()`
method now:

    2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI `<Image/>`
on Android. I also "banned" `GetScaleType()` in `eng/BannedSymbols.txt`.
github-actions bot pushed a commit that referenced this pull request Aug 15, 2025
…3665)

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt
the slowest on Android.

One thing I noticed while scrolling:

    98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
    67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, `PlatformArrange()` is called a lot for every `<Image/>`:

    if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
    {
        var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
        var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
        PlatformView.ClipBounds = clipRect;
    }

`ImageView.ScaleType` is a class, and so and some bookkeeping is done
to lookup *the same* C# instance for a Java object. We can make this a
bit better by writing a new Java method:

    public static boolean isImageViewCenterCrop(@nonnull ImageView imageView) {
        return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
    }

Next, let's make a `PlatformView.ToPixels()` extension method that can
avoid calling `View.Context` for the same reason.

Lastly, we can make a `PlatformInterop.SetClipBounds()` method to avoid
creating a `Android.Graphics.Rect` object in C#.

With these changes, I can only see the topmost `PlatformArrange()`
method now:

    2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI `<Image/>`
on Android. I also "banned" `GetScaleType()` in `eng/BannedSymbols.txt`.
github-actions bot pushed a commit that referenced this pull request Aug 15, 2025
…3665)

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt
the slowest on Android.

One thing I noticed while scrolling:

    98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
    67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, `PlatformArrange()` is called a lot for every `<Image/>`:

    if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
    {
        var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
        var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
        PlatformView.ClipBounds = clipRect;
    }

`ImageView.ScaleType` is a class, and so and some bookkeeping is done
to lookup *the same* C# instance for a Java object. We can make this a
bit better by writing a new Java method:

    public static boolean isImageViewCenterCrop(@nonnull ImageView imageView) {
        return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
    }

Next, let's make a `PlatformView.ToPixels()` extension method that can
avoid calling `View.Context` for the same reason.

Lastly, we can make a `PlatformInterop.SetClipBounds()` method to avoid
creating a `Android.Graphics.Rect` object in C#.

With these changes, I can only see the topmost `PlatformArrange()`
method now:

    2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI `<Image/>`
on Android. I also "banned" `GetScaleType()` in `eng/BannedSymbols.txt`.
github-actions bot pushed a commit that referenced this pull request Aug 19, 2025
…3665)

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt
the slowest on Android.

One thing I noticed while scrolling:

    98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
    67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, `PlatformArrange()` is called a lot for every `<Image/>`:

    if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
    {
        var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
        var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
        PlatformView.ClipBounds = clipRect;
    }

`ImageView.ScaleType` is a class, and so and some bookkeeping is done
to lookup *the same* C# instance for a Java object. We can make this a
bit better by writing a new Java method:

    public static boolean isImageViewCenterCrop(@nonnull ImageView imageView) {
        return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
    }

Next, let's make a `PlatformView.ToPixels()` extension method that can
avoid calling `View.Context` for the same reason.

Lastly, we can make a `PlatformInterop.SetClipBounds()` method to avoid
creating a `Android.Graphics.Rect` object in C#.

With these changes, I can only see the topmost `PlatformArrange()`
method now:

    2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI `<Image/>`
on Android. I also "banned" `GetScaleType()` in `eng/BannedSymbols.txt`.
github-actions bot pushed a commit that referenced this pull request Aug 22, 2025
…3665)

Context: https://github.com/davidortinau/AllTheLists

Profiling @davidortinau's app, I noticed the "Check-ins" sample felt
the slowest on Android.

One thing I noticed while scrolling:

    98.75ms (0.90%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)
    67.11ms (0.61%) Mono.Android!Android.Widget.ImageView.ScaleType.get_CenterCrop()

In this case, `PlatformArrange()` is called a lot for every `<Image/>`:

    if (PlatformView.GetScaleType() == ImageView.ScaleType.CenterCrop)
    {
        var (left, top, right, bottom) = PlatformView.Context!.ToPixels(frame);
        var clipRect = new Android.Graphics.Rect(0, 0, right - left, bottom - top);
        PlatformView.ClipBounds = clipRect;
    }

`ImageView.ScaleType` is a class, and so and some bookkeeping is done
to lookup *the same* C# instance for a Java object. We can make this a
bit better by writing a new Java method:

    public static boolean isImageViewCenterCrop(@nonnull ImageView imageView) {
        return imageView.getScaleType() == ImageView.ScaleType.CENTER_CROP;
    }

Next, let's make a `PlatformView.ToPixels()` extension method that can
avoid calling `View.Context` for the same reason.

Lastly, we can make a `PlatformInterop.SetClipBounds()` method to avoid
creating a `Android.Graphics.Rect` object in C#.

With these changes, I can only see the topmost `PlatformArrange()`
method now:

    2.93ms (0.03%) Microsoft.Maui!Microsoft.Maui.Handlers.ImageHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect)

This should improve the layout performance of all .NET MAUI `<Image/>`
on Android. I also "banned" `GetScaleType()` in `eng/BannedSymbols.txt`.
@github-actions github-actions bot locked and limited conversation to collaborators Sep 7, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-image Image control perf/general The issue affects performance (runtime speed, memory usage, startup time, etc.) (sub: perf) platform/android

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants