1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-25 14:08:34 +01:00

Construct images in UiParser.ParseImage on the main thread to support usage with KNI

This commit is contained in:
Ell 2024-09-14 10:40:11 +02:00
parent 73a02dfe19
commit a6a34c3937
2 changed files with 42 additions and 27 deletions

View file

@ -17,7 +17,9 @@ Jump to version:
## 7.1.1 (In Development) ## 7.1.1 (In Development)
No code changes ### MLEM.Ui
Improvements
- Construct images in UiParser.ParseImage on the main thread to support usage with KNI
## 7.1.0 ## 7.1.0

View file

@ -10,7 +10,6 @@ using MLEM.Ui.Style;
#if NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER #if NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER
using System.Net.Http; using System.Net.Http;
#else #else
using System.Net; using System.Net;
#endif #endif
@ -139,43 +138,59 @@ namespace MLEM.Ui.Parsers {
/// This method invokes an asynchronouns action, meaning the <see cref="Image"/>'s <see cref="Image.Texture"/> will likely not have loaded in when this method returns. /// This method invokes an asynchronouns action, meaning the <see cref="Image"/>'s <see cref="Image.Texture"/> will likely not have loaded in when this method returns.
/// </summary> /// </summary>
/// <param name="path">The absolute, relative or web path to the image.</param> /// <param name="path">The absolute, relative or web path to the image.</param>
/// <param name="onImageFetched">An action that is invoked with the loaded image once it is fetched. Note that this action will be invoked asynchronously.</param> /// <param name="onImageFetched">An action that is invoked with the loaded image once it is fetched. Note that this action will be invoked synchronously.</param>
/// <returns>The loaded image.</returns> /// <returns>The loaded image.</returns>
/// <exception cref="NullReferenceException">Thrown if <see cref="GraphicsDevice"/> is null, or if there is an <see cref="Exception"/> loading the image and <see cref="ImageExceptionHandler"/> is unset.</exception> /// <exception cref="NullReferenceException">Thrown if <see cref="GraphicsDevice"/> is null, or if there is an <see cref="Exception"/> loading the image and <see cref="ImageExceptionHandler"/> is unset.</exception>
protected Image ParseImage(string path, Action<TextureRegion> onImageFetched = null) { protected Image ParseImage(string path, Action<TextureRegion> onImageFetched = null) {
if (this.GraphicsDevice == null) if (this.GraphicsDevice == null)
throw new NullReferenceException("A UI parser requires a GraphicsDevice for parsing images"); throw new NullReferenceException("A UI parser requires a GraphicsDevice for parsing images");
var imageLock = new object(); var bytesLock = new object();
byte[] bytes = null;
TextureRegion image = null; TextureRegion image = null;
return new Image(Anchor.AutoLeft, Vector2.One, _ => { return new Image(Anchor.AutoLeft, Vector2.One, _ => {
lock (imageLock) if (image == null) {
return image; bool bytesNull;
lock (bytesLock)
bytesNull = bytes == null;
if (!bytesNull) {
Texture2D tex;
lock (bytesLock) {
using (var stream = new MemoryStream(bytes))
tex = Texture2D.FromStream(this.GraphicsDevice, stream);
bytes = null;
}
image = new TextureRegion(tex);
onImageFetched?.Invoke(image);
}
}
return image;
}) { }) {
SetHeightBasedOnAspect = true, SetHeightBasedOnAspect = true,
OnAddedToUi = e => { OnAddedToUi = e => {
bool imageNull; if (image == null) {
lock (imageLock) bool bytesNull;
imageNull = image == null; lock (bytesLock)
if (imageNull) bytesNull = bytes == null;
LoadImageAsync(); if (bytesNull)
LoadImageStream();
}
}, },
OnRemovedFromUi = e => { OnRemovedFromUi = e => {
lock (imageLock) { lock (bytesLock)
image?.Texture.Dispose(); bytes = null;
image = null; image?.Texture.Dispose();
} image = null;
} }
}; };
async void LoadImageAsync() { async void LoadImageStream() {
// only apply the base path for relative files // only apply the base path for relative files
if (this.ImageBasePath != null && !path.StartsWith("http") && !Path.IsPathRooted(path)) if (this.ImageBasePath != null && !path.StartsWith("http") && !Path.IsPathRooted(path))
path = $"{this.ImageBasePath}/{path}"; path = $"{this.ImageBasePath}/{path}";
try { try {
Texture2D tex; byte[] src;
if (path.StartsWith("http")) { if (path.StartsWith("http")) {
byte[] src;
#if NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER #if NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER
using (var client = new HttpClient()) using (var client = new HttpClient())
src = await client.GetByteArrayAsync(path); src = await client.GetByteArrayAsync(path);
@ -183,18 +198,16 @@ namespace MLEM.Ui.Parsers {
using (var client = new WebClient()) using (var client = new WebClient())
src = await client.DownloadDataTaskAsync(path); src = await client.DownloadDataTaskAsync(path);
#endif #endif
using (var memory = new MemoryStream(src))
tex = Texture2D.FromStream(this.GraphicsDevice, memory);
} else { } else {
using (var stream = Path.IsPathRooted(path) ? File.OpenRead(path) : TitleContainer.OpenStream(path)) using (var fileStream = Path.IsPathRooted(path) ? File.OpenRead(path) : TitleContainer.OpenStream(path)) {
tex = Texture2D.FromStream(this.GraphicsDevice, stream); using (var memStream = new MemoryStream()) {
} await fileStream.CopyToAsync(memStream);
lock (imageLock) { src = memStream.ToArray();
if (image == null) { }
image = new TextureRegion(tex);
onImageFetched?.Invoke(image);
} }
} }
lock (bytesLock)
bytes = src;
} catch (Exception e) { } catch (Exception e) {
if (this.ImageExceptionHandler != null) { if (this.ImageExceptionHandler != null) {
this.ImageExceptionHandler.Invoke(path, e); this.ImageExceptionHandler.Invoke(path, e);