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

[Spec] ImageSourceHandler #480

Open
pictos opened this issue Mar 11, 2021 · 14 comments
Open

[Spec] ImageSourceHandler #480

pictos opened this issue Mar 11, 2021 · 14 comments

Comments

@pictos
Copy link
Contributor

pictos commented Mar 11, 2021

[ImageSource]

Create a service that handles the ImageSource logic on shared code based on this discussion between me, @PureWeen, and @mattleibow.

The general idea is to have an ImageSource handler class that will handle all ImageSource updates in the shared layer and reflects them to the Control.

API

[ IImageSourceHandler]

interface IImageSourceHandler
{
    ImageSource Current {get;}
    ImageSource LoadingPlaceholder {get;}
    ImageSource ErrorPlaceholder {get;}
    ImageSource Desired {get;}

    // Those can be internals

    Action OnLoadingStarted {get;}
    Action OnSourceChanged {get;}
    Action OnLoadingFailed {get;}
    Action OnLoadingCompleted {get;}
}

Properties

API Description
Current This will be the current ImageSource for displayed Image.
LoadingPlaceholder ImageSource that will be presented while we load/download the Desired ImageSource
ErrorPlaceholder ImageSource that will be presented if something goes wrong with the Desired ImageSource
Desired The ImageSource that the user wants to present in the control

Events

API Description
OnLoadingStarted Action that will be fired when we start to load the Desired ImageSource
OnSourceChanged Action that will be fired when Current value changes
OnLoadingFailed Action that will be fired when something goes wrong during the Loading process
OnLoadingCompleted Action that will be fired when we loaded the Desired ImageSource successfully

Scenarios

<Image Desired="ImageThatIWant"
       LoadingPlaceholder="LoadingTheDesiredSource"
       ErrorPlaceholder="ImageIfSomethingGoesWrong" />

Backward Compatibility

Minimum API levels?
Breaking changes?
Unsupported platforms?

Difficulty: medium

@mattleibow
Copy link
Member

I am looking at this now and I might not be thinking outside the box enough. With things like WinUI/UWP, it appears the ImageSource sometimes has events, for example:

  • BitmapImage has DownloadProgress, ImageFailed and ImageOpened
  • SvgImageSource has Opened and OpenFailed

The nice thing is that there are events tied directly to the source that it is happening on.

However, for MAUI, this is a bit different as the ImageSource is more some "key" or the object that the service uses to load the image.

As it is now, the <Image> or the <ImageButton> will have the events as the handler will pass the things on to the service, and then the service can fire all the events as it goes.

In order to get consistent events on the ImageSource, we will have to store the result of the service (such as the Drawable on Android) in the ImageSource so that the loading event fires just once and then never again. However, this is hard to do because the are some "fast paths" or "optimized paths" that do not use a "bitmap" object directly.

For example, on Android, there is the service that directly gets the view to do the work. We are going to use Glide for this now but there is no bitmap object to store in the ImageSource. This will mean that the various loading and cancelled events will fire randomly if the same ImageSource is used multiple times. On Windows, many times we literally wrap the MAUI ImageSource in the WinUI ImageSource and no objects are created at all.

@pictos
Copy link
Contributor Author

pictos commented Apr 17, 2021

@mattleibow, thinking aloud here.

If inside the ImageSource we have a Stream or byte array that stores the current Bitmap (but could be any image file), and if the dev uses it in more than one place all they will see the same Image File and the same state (Loading, Loaded, Error, etc).

With this approach, we need to care about disposing of that Stream, not sure if this easy to do. Also, making it immutable can reduce the complexity (?)

@mattleibow
Copy link
Member

@pictos that was my thinking, however, based on the things I see that libraries do this might not always work out. For example, if we have a look at the way Glide works, they have a "optimized path" that really says "load this URI into this view" and does not need us to interact with any bitmap objects at all.

This also restrict the caching mechanisms. For example, if you want to have a tiered cache with say a memory and then a disk cache (like Glide does today), we will have to make the ImageSource objects more complex as they will now have to either do this caching, or interact with services to find some cache mechanism if the dev wants to override.

If we have the image handler do all the work, then the ImageSource is very simple, and any custom handler can use a complete library (Glide) or have a combination of image loader and caching systems.

@mattleibow
Copy link
Member

mattleibow commented Apr 18, 2021

One thing that I did come across was a way to release objects. Glide ties a bitmap reference count to the target. This could be the image or the image view. For example, if I load a bitmap into an image view, Glide will mark that bitmap as referenced 1 time. When I clear the image view (say if Source = null) then I need to go back to Glide and say deref the view/bitmap. This is easier with image views in some ways.

However, this is harder with other views/objects. For example, a Button can have an image, and the only way we can do that is by asking Glide for a Drawable instance, and then manually assigning it to the native button. To do this, we have an ITarget object, which is a custom Glide object that is used by us to track the image loading - and we get the Drawable from that ITarget object. When the dev clears the image (or changes it), then we need to go back to Glide and say "we are no longer using this ITarget object" and then it goes and derefs it.

I was thinking that we could go add a ref to this target object in the ImageSource? Maybe like a "cache key" and then the various handlers can first check that if they want before going to the underlying loading library. The issue is still, how do we clear the image? Is there a way to know when there are no more references to the ImageSource and then we can pass it back to the image library and say "we are done with this key"?

@pictos
Copy link
Contributor Author

pictos commented Apr 20, 2021

@mattleibow yeah, I think that having a target object reference to the ImageSource could be great, can be something simple like an ImageSourceManager with a global ConcurrentDictionary<string, (object img, int count)> ImagesCache where the key is the source, like "dotnet_bot.png" or the URL, and a count that increases every time that some control uses that ImageSource. And when we release the control, we check if there's an image reference and decrease the count, when the count is zero we can remove it from the ImagesCache.

Probably this will end with something more robust but on my silly mind that can work, thoughts?

@mattleibow
Copy link
Member

My PR that was just merged has the basics in place - services and events. The actual events and placeholders on the control can come later. I have an events API that can be used to get notified of events on the control and images can be swapped out at any time.

@mattleibow
Copy link
Member

I am thinking for this we actually may want to have a separate control that uses the VSM to do this thing. Depending on status, it can switch between states - I think the WCT does this with ImageEx

@Redth Redth added the area-image Image loading, sources, caching label Aug 5, 2021
@dotnet dotnet deleted a comment Aug 5, 2021
@Redth Redth added this to the .NET 7 milestone Sep 8, 2021
@PureWeen
Copy link
Member

PureWeen commented Nov 5, 2021

#1893

@Syed-RI
Copy link

Syed-RI commented Jul 19, 2022

Hmm I take it we are not getting these features then?

@EdwardMcFlurry
Copy link

@Redth isn’t .NET 8 a little bit too far to have this working right now, or is it out already or know any workaround?

Just curious.

@karthikraja-arumugam
Copy link

karthikraja-arumugam commented May 10, 2023

Do we have any workaround for this? We need a notification when the image has been successfully loaded into view.

@samhouts samhouts modified the milestones: .NET 8 Planning, Under Consideration Aug 16, 2023
@e-sly
Copy link

e-sly commented Mar 1, 2024

Any update on when the loading and error placeholder will be implemented?

@EdwardMcFlurry
Copy link

Team, do know perhaps the timeline to at least expect this functionality or some workaround?

@Eilon Eilon added the area-controls-image Image control label May 10, 2024
@Eilon Eilon removed the area-image Image loading, sources, caching label May 10, 2024
@PureWeen PureWeen modified the milestones: Under Consideration, Backlog Aug 2, 2024
@RobK410
Copy link

RobK410 commented Sep 7, 2024

Certainly would be helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests