uno icon indicating copy to clipboard operation
uno copied to clipboard

BitmapImage returns 0 for decoded and pixel dimensions

Open marcelwgn opened this issue 4 years ago • 9 comments

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:

  • [x] Uno.UI / Uno.UI.WebAssembly / Uno.UI.Skia
  • [ ] Uno.WinUI / Uno.WinUI.WebAssembly / Uno.WinUI.Skia
  • [ ] Uno.SourceGenerationTasks
  • [ ] Uno.UI.RemoteControl / Uno.WinUI.RemoteControl
  • [x] Uno.Core

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

  • [ ] iOS
  • [x] Android
  • [ ] WebAssembly
  • [ ] WebAssembly renderers for Xamarin.Forms
  • [ ] macOS
  • [ ] Skia
    • [ ] WPF
    • [ ] GTK (Linux)
    • [ ] Tizen
  • [ ] Windows
  • [ ] Build tasks
  • [ ] Solution Templates

IDE:

  • [ ] Visual Studio 2017 (version: )
  • [x] Visual Studio 2019 (version: 16.8.5)
  • [ ] Visual Studio for Mac (version: )
  • [ ] Rider Windows (version: )
  • [ ] Rider macOS (version: )
  • [ ] Visual Studio Code (version: )

Relevant plugins:

  • [ ] Resharper (version: )

Anything else we need to know?

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

marcelwgn avatar Feb 13 '21 18:02 marcelwgn

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?

Provides access to the image-related properties of an item (like a file or folder).

CesarRabelo avatar Jan 27 '22 00:01 CesarRabelo

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
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...

jeromelaban avatar Jan 27 '22 01:01 jeromelaban

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.

DanielCauser avatar Jan 27 '22 14:01 DanielCauser

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

roxk avatar Sep 05 '22 10:09 roxk

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!

roxk avatar Sep 05 '22 11:09 roxk

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.

jeromelaban avatar Sep 06 '22 00:09 jeromelaban

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 avatar Sep 06 '22 08:09 roxk

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!

roxk avatar Sep 06 '22 09:09 roxk

@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 avatar Sep 06 '22 10:09 MartinZikmund

This is done and merged in 4x

MartinZikmund avatar Aug 04 '23 07:08 MartinZikmund