unoplatform / uno

Open-source platform for building cross-platform native Mobile, Web, Desktop and Embedded apps quickly. Create rich, C#/XAML, single-codebase apps from any IDE. Hot Reload included! 90m+ NuGet Downloads!!
https://platform.uno
Apache License 2.0
8.87k stars 716 forks source link

`BitmapImage` returns 0 for decoded and pixel dimensions #5238

Closed marcelwgn closed 1 year ago

marcelwgn commented 3 years ago

Current behavior

Using the following code, you can capture and "create" a Bitmap image to use as source for an image for example:

var captureUI = new CameraCaptureUI();
var photo = await captureUI.CaptureFileAsync(CameraCaptureUIMode.Photo);

if (photo == null)
{
    return;
}
 var source = new BitmapImage(new Uri(photo.Path));
ImageViewer.Source = source;

However source.PixelHeight,source.PixelWidth, source.DecodePixelHeight and source.DecodePixelWidth are 0.

Expected behavior

These properties should be not 0 and usable, e.g. to use them to size correctly or convert a bytestream into a different image object.

How to reproduce it (as minimally and precisely as possible)

See code example above.

Workaround

No workarounds found so far. A possible workaround would have been to use GetImagePropertiesAsync to get the necessary information, unfortunately this isn't implemented yet.

Environment

Nuget Package:

Nuget Package Version(s): Uno.UI: 3.5.0 Uno.Core: 2.1.0 Affected platform(s):

IDE:

Relevant plugins:

Anything else we need to know?

Found while trying to create a RGBLuminanceSource to use for QR code scanning.

CesarRabelo commented 2 years ago

Hello guys, I'm thinking in implementing the method GetImagePropertiesAsync that was suggestion on workaround. https://docs.microsoft.com/en-us/uwp/api/windows.storage.fileproperties.imageproperties?view=winrt-19041 Would this be the best way?

ImageProperties Class (Windows.Storage.FileProperties) - Windows UWP applications
Provides access to the image-related properties of an item (like a file or folder).
jeromelaban commented 2 years ago

You will not be able to use this API to get image properties, as images may come from network sources, local files, local streams, or from computed images.

Each platform has its own ways to get image sizes, depending on what's done in all ImageSource.*.cs implementations in https://github.com/unoplatform/uno/blob/2a76c3be2e5ff5325e37e50f4fb54b7724359906/src/Uno.UI/UI/Xaml/Media.

GitHub
uno/src/Uno.UI/UI/Xaml/Media at 2a76c3be2e5ff5325e37e50f4fb54b7724359906 · unoplatform/uno
Build Mobile, Desktop and WebAssembly apps with C# and XAML. Today. Open source and professionally supported. - uno/src/Uno.UI/UI/Xaml/Media at 2a76c3be2e5ff5325e37e50f4fb54b7724359906 · unoplatfor...
DanielCauser commented 2 years ago

Hey @CesarRabelo,

Taking into account what @jeromelaban mentioned, I think you could take the following approach:

  1. Reproduce the reported issue
  2. Check the existing Android implementation and why it does not add values to the properties.
  3. Check the appropriate way to provide those values on the Android platform.
roxk commented 2 years ago

It affects wasm as well. No workaround means it is a show-stopper for now.

roxk commented 2 years ago

I'm about to implement a workaround by creating an HtmlImage and set the src manually. However, the class is not found, and when I write new UIElement("img"), the compiler complains there is no such constructor. Checking the source, everything is public so they should be accessible. Am I missing anything? Thanks!

jeromelaban commented 2 years ago

HtmlImage is not meant to be used directly and is hidden from the public APIs accessible from the apps, but you can create your own control based on img if you need to.

Why do you need the size of the image, out of curiosity? On WebAssembly, since Uno is giving Urls directly to the control, we don't really have a way to get the image size in the same way UWP does. The Image control gets the size once the image has been loaded, but can't get it before that.

roxk commented 2 years ago

I need it to compute scale informations to control zoom and scaling. I cant use scroller viewer becoz scrollerviewer's scale in wasm isnt working 😅.

In js, we can instantiate Image and set src to force it to load and grab size there, and I think we can do similar things with HtmlImage. Long term, bitmap source should use similar implementation (better yet, implement BitmapEncoder and use it in bitmap source), so others can get the size easily. I dont mind submiting PRs to fix, but being unable to workaround this for now is a show stopper.

I might try calling js using wasm runtime js interop but it is really not ideal. Being able to use wasm specific class like HtmlImage directly is better. What is the reasoning behind hiding implementation specific class? Users would need to guard such code use with #if anyways.

roxk commented 2 years ago

For those who are affected by this issue and is on wasm, you can use this workaround to get image size:

#if __WASM__
                var result = await WebAssemblyRuntime.InvokeAsync("(async () => {"
                    + "var promise = new Promise((resolve) => {"
                        + "var i = new Image();"
                        + "i.onload = function() { resolve(\"\" + i.width + \",\" + i.height); };"
                        + $"i.src = \"{_imageSource.Uri}\";"
                    + "});"
                    + "return await promise;"
                    + "})()");
                var sizes = result.Split(",");
                if (sizes.Length != 2)
                {
                    throw new InvalidOperationException($"Failed to get image size via js interop. Result: {result}");
                }
                const int widthIndex = 0;
                const int heightIndex = 1;
                Width = uint.Parse(sizes[widthIndex]);
                Height = uint.Parse(sizes[heightIndex]);

I think BitmapSource etc should get the image size accordingly before ImageOpened is called. Should I submit a PR? @jeromelaban

Possible fixes:

  1. Fix all BitmapSource by getting image size using method above.
  2. Implement BitmapEncoder, at least getting image size, using method above. Then, fix all BitmapSource by using BitmapEncoder to get size.

Engineering wise I think (2) is a better solution, but I reckon it might not be feasible due to API surface concerns, etc. Please lemme know which works for Uno so I can proceed to work on either one of these (I'd like to omit this workaround in my codebase). If I should open a new issue for the implementation, please lemme know. Thanks!

MartinZikmund commented 2 years ago

@roxk There is a bigger refactoring in progress for ImageSource and related classes inside the SVG PR I have open so I suggest waiting for it to be merged before submitting this change, but it definitely would be great if you were able to contribute it afterwards 🚀 !

MartinZikmund commented 1 year ago

This is done and merged in 4x