Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request to back port fix into .NET 7 #17921

Closed
williambuchanan2 opened this issue Oct 9, 2023 · 10 comments
Closed

Request to back port fix into .NET 7 #17921

williambuchanan2 opened this issue Oct 9, 2023 · 10 comments
Labels
platform/android 🤖 s/needs-attention Issue has more information and needs another look t/bug Something isn't working

Comments

@williambuchanan2
Copy link

Description

This issue:

#14052

Is basically a show stopper for any .NET 7 app which has images in it (i.e. pretty much every app). Can someone review the decision not to back port the long awaited fix into .NET 7.

Upgrading to .NET 8 is not an option because a) it doesn't work, and b) the number of new bugs required to work around, and c) it's not live yet.

So at the moment this is a complete show stopper.

Steps to Reproduce

Add image to app.
Run app.

Link to public reproduction project repository

?

Version with bug

7.0.92

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

7.0.92

Affected platforms

Android

Affected platform versions

Android 11 and 13

Did you find any workaround?

No

Relevant log output

No response

@williambuchanan2 williambuchanan2 added the t/bug Something isn't working label Oct 9, 2023
@jonmdev
Copy link

jonmdev commented Oct 10, 2023

Oh wow, looks like you found the same catastrophic issue I did. I discussed a temporary workaround in my thread.

You can create your own Image loading Behavior or class by accessing the PlatformView of an Image and then loading the resource/photo into that directly in the platform's respective format. If you need some more pointers on how to do it, feel free to message me. Took me most of a day to figure it out. I don't want to share my whole code though for IP reasons.

#17857

My repro project can reproduce this issue very rapidly and consistently at least.

But yes, it is absolutely insane that the glitch exists in the first place and I can't believe they've known about it for 6 months and people like me are still having to stumble into it.

I wrote a workaround myself in less than a day. Can you PM on Github? Feel free to PM me if needed, otherwise try what I did. UIImage and Android Bitmap can both be made easily from SkiaSharp Views. Load the image into an SKBItmap then there are extensions:

https://learn.microsoft.com/en-us/dotnet/api/skiasharp.views.ios.iosextensions.touiimage?view=skiasharp-views-2.88
https://learn.microsoft.com/en-us/dotnet/api/skiasharp.views.android.androidextensions.tobitmap?view=skiasharp-views-2.88

Those can be set directly into the UIImageView (iOS) and ImageView (Android) objects underneath.
Windows is a bit trickier, but if you load the image into a filestream, then memorystream, and convert the memory stream to random access, you can then create an empty BItmapImage() and use that random access memory stream as the source for it.

Yeah.

@williambuchanan2
Copy link
Author

Oh wow, looks like you found the same catastrophic issue I did. I discussed a temporary workaround in my thread.

Hi @jonmdev this is great news. I will have a look at your workaround. While looking at your thread I noticed that they say they ported it into .NET7 a few weeks ago. I wonder if that has actually happened and when we will finally see it being released.

@jsuarezruiz jsuarezruiz added the backport/suggested The PR author or issue review has suggested that the change should be backported. label Oct 10, 2023
@PureWeen
Copy link
Member

the fix for 14502 was backported here #16640 and should be available in the latest version of .NET7 released yesterday

@PureWeen PureWeen added s/needs-info Issue needs more info from the author and removed backport/suggested The PR author or issue review has suggested that the change should be backported. labels Oct 11, 2023
@ghost
Copy link

ghost commented Oct 11, 2023

Hi @williambuchanan2. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@williambuchanan2
Copy link
Author

williambuchanan2 commented Oct 11, 2023

Seems not....

image

image

image

image

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-info Issue needs more info from the author labels Oct 11, 2023
@jonmdev
Copy link

jonmdev commented Oct 11, 2023

@williambuchanan2 If you're working by C# predominantly or can manage the image loading with a C# Behavior script and they're not making progress on this one in any timely fashion, let me know and I can post a basic script for an Image loading Behavior that circumvents this. If they're fixing it ASAP probably no need for you to work around it but it's doable if needed.

From what I see the three most insanely devastating glitches in MAUI currently are:

  1. This one - which cripples Image loading in Android unless you code an alternative method also as described in my thread here: [Android] Image.Source = ImageSource.FromResource() & ImageSource.FromStream() are not reliably integrating back with the MainThread #17857
  2. Editor being completely fried in iOS which makes it impossible to build anything with user input in iOS as per: [iOS] Editor autoscrolls its text view completely off the screen if you press 'return' too many times, has no constraint against scrolling the text view off screen. Objects lag in resizing around it. #17757
  3. Entire words and letters going missing in Android labels as per: [Android] Entire words omitted & letters truncated from Label display #17884

I say this with love because I want Microsoft and the MAUI team to succeed at this. I really like MAUI and it fills an important and unique need.

But man it is crazy. How can one develop or release anything in MAUI even just with these three glitches alone? You will have an app that can't take user text input in iOS, can't display images in Android, and can't even show your Terms of Service with any legal guarantees in Android (is a TOS legally binding if words are randomly missing or it is truncated?).

On the plus side, Android/iOS are not rapidly changing systems, so once this stuff is fixed it should be good for years. At least in theory. I remain optimistic. They are working on it. At least so it seems. 🙂

To Microsoft staff, if it helps, my bug report for this issue (Android loading thread glitch) can reproduce the issue every time just by pressing play and then clicking the screen 1-5 times. You can see that in my thread linked above.

@williambuchanan2
Copy link
Author

@williambuchanan2 If you're working by C# predominantly or can manage the image loading with a C# Behavior script and they're not making progress on this one in any timely fashion, let me know and I can post a basic script for an Image loading Behavior that circumvents this. If they're fixing it ASAP probably no need for you to work around it but it's doable if needed.

Hi @jonmdev thanks for the information. Yes this problem is an absolute show stopper, and also makes debugging almost impossible (I can do around 3 taps max before it crashes, so I need to plan my debug sessions carefully).

I raised this in March and spent several months waiting for a fix which they said was coming. It then turned out that they decided not to back port it. Long story short - 7 months later I am still having to debug 3 taps max at a time and still can't release the app because of it. So I think the reality is I will need to use your workaround. I had been holding off after seeing it was back ported, but looks like it hasn't fixed the problem.

BTW, this is just one of many major bugs which haven't been addressed. Some of them are 18 months + old. If you are planning on using Maui be prepared to lose a lot of time.

@jonmdev
Copy link

jonmdev commented Oct 12, 2023

@williambuchanan2 If you're working by C# predominantly or can manage the image loading with a C# Behavior script and they're not making progress on this one in any timely fashion, let me know and I can post a basic script for an Image loading Behavior that circumvents this. If they're fixing it ASAP probably no need for you to work around it but it's doable if needed.

Hi @jonmdev thanks for the information. Yes this problem is an absolute show stopper, and also makes debugging almost impossible (I can do around 3 taps max before it crashes, so I need to plan my debug sessions carefully).

I raised this in March and spent several months waiting for a fix which they said was coming. It then turned out that they decided not to back port it. Long story short - 7 months later I am still having to debug 3 taps max at a time and still can't release the app because of it. So I think the reality is I will need to use your workaround. I had been holding off after seeing it was back ported, but looks like it hasn't fixed the problem.

BTW, this is just one of many major bugs which haven't been addressed. Some of them are 18 months + old. If you are planning on using Maui be prepared to lose a lot of time.

Maybe we're all doomed here then. 😂 Please Microsoft, please fix MAUI.

Here is my loader script for synchronus loading (not async). Took me a few days to figure out which wasn't wasted as at least I learned some basic principles. The worst part was figuring out how to make a BitmapImage for Windows.

Works in Windows/iOS/Android as far as I know. Could run the operation as a Task to make it async also.

Here is the code for an ImageLoaderBehavior that can circumvent the broken MAUI Image file loading:

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

#if IOS
using NativeBitmap = UIKit.UIImage;
using NativeImageView = UIKit.UIImageView;
using UIKit;
#elif ANDROID
using NativeBitmap = Android.Graphics.Bitmap;
using NativeImageView = Android.Widget.ImageView;

#elif WINDOWS
using NativeBitmap = Microsoft.UI.Xaml.Media.Imaging.BitmapImage;
using NativeImageView = Microsoft.UI.Xaml.Controls.Image;
using Microsoft.UI.Xaml.Media.Imaging;
#else
using NativeBitmap = object;
using NativeImageView = object;
#endif

namespace Image_Loader_Demo {
    public class ImageLoaderBehavior: Behavior<Image> {

        Image image = null;
        string resourceName = null;
        NativeBitmap nativeBitmap = null;
        NativeImageView nativeImageView = null;

        public int rawWidth;
        public int rawHeight;
        protected override void OnAttachedTo(Image bindable) {
            this.image = bindable;
            base.OnAttachedTo(bindable);
            if (image.Handler != null) {
                nativeImageView = image.ToPlatform(image.Handler.MauiContext) as NativeImageView;

                if (resourceName != null) {
                    loadImageFromResources(resourceName);
                }
            }
            else {
                image.HandlerChanged += delegate {
                    ImageHandler imageHandler = image.Handler as ImageHandler; 
                    var imageHandlerPlatView = imageHandler.PlatformView;
                    nativeImageView = imageHandlerPlatView;

                    if (resourceName != null) {
                        loadImageFromResources(resourceName);
                    }
                };
            }
        }
        protected override void OnDetachingFrom(Image bindable) {
            this.image = null;
            base.OnDetachingFrom(bindable);
        }

        public void loadImageFromResources(string resourceName) {
            this.resourceName = resourceName;
            if (resourceName != null) {

                Assembly assembly = GetType().GetTypeInfo().Assembly;

#if WINDOWS
                nativeBitmap = new();
                nativeBitmap.CreateOptions = BitmapCreateOptions.None;
                using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName)) {
                    using (var memoryStream = new MemoryStream()) { 
                        resourceStream.CopyTo(memoryStream);
                        var randomStream = memoryStream.AsRandomAccessStream();
                        randomStream.Seek(0);
                        nativeBitmap.SetSource(randomStream);
                        rawWidth = nativeBitmap.PixelWidth;
                        rawHeight = nativeBitmap.PixelHeight;
                        Debug.WriteLine("GOT NATIVE WINDOWS IMAGE " + rawWidth + " " + rawHeight);
            
                    }
                }


#endif
#if ANDROID
                using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName)) {

                    nativeBitmap = Android.Graphics.BitmapFactory.DecodeStream(resourceStream);
                    rawWidth = nativeBitmap.Width;
                    rawHeight = nativeBitmap.Height;
                    Debug.WriteLine("GOT NATIVE ANDROID IMAGE " + rawWidth + " " + rawHeight);
                }

#endif
#if IOS
                using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName)) {

                    using (var imageData = Foundation.NSData.FromStream(resourceStream)) { //https://stackoverflow.com/questions/43995539/how-to-convert-stream-to-uiimage-in-xamarin-ios
                        nativeBitmap = UIImage.LoadFromData(imageData);

                        rawWidth = (int)nativeBitmap.Size.Width; //https://stackoverflow.com/questions/5249664/how-can-i-get-the-height-and-width-of-an-uiimage
                        rawHeight = (int)nativeBitmap.Size.Height;
                        Debug.WriteLine("GOT NATIVE IOS IMAGE " + rawWidth + " " + rawHeight);
                    }
                }
#endif
                if (nativeImageView != null) {
                    applyNativeBitmapToNativeView();
                }
            }

        }
        private void applyNativeBitmapToNativeView() {
            MainThread.BeginInvokeOnMainThread(new Action(() => {

                //APPLY NATIVE BITMAP
#if ANDROID
                if (nativeImageView != null) {
                    nativeImageView.SetImageBitmap(nativeBitmap);
                    nativeBitmap.Dispose(); 

                }
#elif IOS
                if (nativeImageView != null) {
                    nativeImageView.Image = nativeBitmap;
                    nativeBitmap.Dispose(); 
                }
#elif WINDOWS
                if (nativeImageView != null) {
                    nativeImageView.Source = nativeBitmap;
                    //nativeBitmap = null; //I AM NOT SURE HOW TO GET RID OF THESE AND NOT MEMORY LEAK...
                }
#endif

            }));

        }

    }
}

To use it you will also need to add to your csproj to enable "Aliases" function:

  <PropertyGroup>
    <LangVersion>preview</LangVersion>
  </PropertyGroup>

Then in practice you can go:

Image image = new();
ImageLoadBehavior loader = new();
image.Behaviors.Add(loader);
loader.loadImageFromResources(resourceName); //could also run this as Task for async I believe

And it should work find.

Crazy we are supposed to rewrite basic functions. And I don't know how to fix the other broken basic functions like the Editor and Label issues I mentioned.

Hope that helps.

Let's pray for MAUI. 😁

If anyone is reading this and knows how to properly dispose of BitmapImage in Windows I am eager to know. Thanks.

@SliemBeji-FBC
Copy link

Microsoft please fix this....

@williambuchanan2
Copy link
Author

Appears to have actually been fixed in .NET 7 now so closing off

@github-actions github-actions bot locked and limited conversation to collaborators Dec 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
platform/android 🤖 s/needs-attention Issue has more information and needs another look t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants