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

Compare commits

...

6 commits

Author SHA1 Message Date
Ell
c5f4fd27bd bump upcoming version 2024-09-14 11:48:18 +02:00
Ell
44388c0ebd Merge remote-tracking branch 'origin/main' 2024-09-14 11:39:30 +02:00
Ell
cdc4756a6a 7.1.1 2024-09-14 11:39:05 +02:00
Ell
3fc42143d5
Added web demo to the website (#24)
* work on publishing the web demo

* some fixes

* link to web demo
2024-09-14 11:37:06 +02:00
Ell
c4bd03ff04 Image loading improvements:
- Added TextureExtensions.PremultipliedCopy for textures
- Create a premultiplied copy of UiParser images to support usage with KNI
2024-09-14 10:48:04 +02:00
Ell
a6a34c3937 Construct images in UiParser.ParseImage on the main thread to support usage with KNI 2024-09-14 10:40:11 +02:00
10 changed files with 167 additions and 66 deletions

View file

@ -20,32 +20,10 @@ jobs:
uses: android-actions/setup-android@v3 uses: android-actions/setup-android@v3
- name: Restore tools - name: Restore tools
run: dotnet tool restore run: dotnet tool restore
- name: Run cake - name: Run Publish
uses: coactions/setup-xvfb@v1 uses: coactions/setup-xvfb@v1
with: with:
run: dotnet cake --target Publish --ref ${{ github.ref }} --buildNum ${{ github.run_number }} run: dotnet cake --target Publish --ref ${{ github.ref }} --buildNum ${{ github.run_number }}
env: env:
NUGET_KEY: ${{ secrets.NUGET_KEY }} NUGET_KEY: ${{ secrets.NUGET_KEY }}
BAGET_KEY: ${{ secrets.BAGET_KEY }} BAGET_KEY: ${{ secrets.BAGET_KEY }}
docs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Restore tools
run: dotnet tool restore
- name: Run cake
run: dotnet cake --target Document --ref ${{ github.ref }} --buildNum ${{ github.run_number }}
- name: Deploy
if: startsWith(github.ref, 'refs/tags/')
# this is a beautiful way to deploy a website and i will not take any criticism
run: |
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb && sudo dpkg -i cloudflared.deb
mkdir ~/.ssh && echo "${{ secrets.ELLBOT_KEY }}" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
rsync -rv --delete -e 'ssh -o "ProxyCommand cloudflared access ssh --hostname %h" -o "StrictHostKeyChecking=no"' Docs/_site/. ellbot@ssh.ellpeck.de:/var/www/MLEM

78
.github/workflows/web.yml vendored Normal file
View file

@ -0,0 +1,78 @@
on: [push, pull_request]
jobs:
build-demo:
runs-on: windows-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Restore tools
run: dotnet tool restore
- name: Run PublishWeb
run: dotnet cake --target PublishWeb --ref ${{ github.ref }} --buildNum ${{ github.run_number }}
- name: Upload demo artifact
uses: actions/upload-artifact@v4
with:
path: Demos.Web/bin/Release/net8.0/publish/wwwroot
name: demo
include-hidden-files: true
if-no-files-found: error
build-docs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Restore tools
run: dotnet tool restore
- name: Run Document
run: dotnet cake --target Document --ref ${{ github.ref }} --buildNum ${{ github.run_number }}
- name: Upload docs artifact
uses: actions/upload-artifact@v4
with:
path: Docs/_site
name: docs
include-hidden-files: true
if-no-files-found: error
deploy:
needs: [build-demo, build-docs]
runs-on: ubuntu-latest
steps:
- name: Download demo artifact
uses: actions/download-artifact@v4
with:
name: demo
path: demo
- name: Download docs artifact
uses: actions/download-artifact@v4
with:
name: docs
path: docs
- name: Combine sites
run: |
mv docs _site
mv demo _site/demo
- name: Upload combined artifact
uses: actions/upload-artifact@v4
with:
path: _site
name: site
include-hidden-files: true
if-no-files-found: error
- name: Deploy
if: startsWith(github.ref, 'refs/tags/')
# this is a beautiful way to deploy a website and i will not take any criticism
run: |
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb && sudo dpkg -i cloudflared.deb
mkdir ~/.ssh && echo "${{ secrets.ELLBOT_KEY }}" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
rsync -rv --delete -e 'ssh -o "ProxyCommand cloudflared access ssh --hostname %h" -o "StrictHostKeyChecking=no"' _site/. ellbot@ssh.ellpeck.de:/var/www/MLEM

View file

@ -2,7 +2,8 @@
MLEM tries to adhere to [semantic versioning](https://semver.org/). Potentially breaking changes are written in **bold**. MLEM tries to adhere to [semantic versioning](https://semver.org/). Potentially breaking changes are written in **bold**.
Jump to version: Jump to version:
- [7.1.1](#711-in-development) - [7.1.2](#712-in-development)
- [7.1.1](#711)
- [7.1.0](#710) - [7.1.0](#710)
- [7.0.0](#700) - [7.0.0](#700)
- [6.3.1](#631) - [6.3.1](#631)
@ -15,10 +16,21 @@ Jump to version:
- [5.1.0](#510) - [5.1.0](#510)
- [5.0.0](#500) - [5.0.0](#500)
## 7.1.1 (In Development) ## 7.1.2 (In Development)
No code changes No code changes
## 7.1.1
### MLEM
Additions
- Added TextureExtensions.PremultipliedCopy for textures
### MLEM.Ui
Improvements
- Construct images in UiParser.ParseImage on the main thread to support usage with KNI
- Create a premultiplied copy of UiParser images to support usage with KNI
## 7.1.0 ## 7.1.0
### MLEM ### MLEM

View file

@ -8,7 +8,8 @@
"**/MLEM**.csproj" "**/MLEM**.csproj"
], ],
"exclude": [ "exclude": [
"**.FNA.**" "**.FNA.**",
"**.KNI.**"
] ]
} }
], ],

View file

@ -6,3 +6,5 @@
href: api/ href: api/
- name: Changelog - name: Changelog
href: ../CHANGELOG.md href: ../CHANGELOG.md
- name: Web Demo
href: demo/

View file

@ -17,18 +17,8 @@ namespace MLEM.Data.Content {
#endif #endif
// premultiply the texture's color to be in line with the pipeline's texture reader // premultiply the texture's color to be in line with the pipeline's texture reader
using (var texture = Texture2D.FromStream(manager.GraphicsDevice, stream)) { using (var texture = Texture2D.FromStream(manager.GraphicsDevice, stream))
var ret = new Texture2D(manager.GraphicsDevice, texture.Width, texture.Height); return texture.PremultipliedCopy();
using (var textureData = texture.GetTextureData()) {
using (var retData = ret.GetTextureData()) {
for (var x = 0; x < ret.Width; x++) {
for (var y = 0; y < ret.Height; y++)
retData[x, y] = Color.FromNonPremultiplied(textureData[x, y].ToVector4());
}
}
}
return ret;
}
} }
/// <inheritdoc /> /// <inheritdoc />

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,61 @@ 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) {
bool bytesNull;
lock (bytesLock)
bytesNull = bytes == null;
if (!bytesNull) {
Texture2D tex;
lock (bytesLock) {
using (var stream = new MemoryStream(bytes)) {
using (var read = Texture2D.FromStream(this.GraphicsDevice, stream))
tex = read.PremultipliedCopy();
}
bytes = null;
}
image = new TextureRegion(tex);
onImageFetched?.Invoke(image);
}
}
return 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)
bytes = null;
image?.Texture.Dispose(); image?.Texture.Dispose();
image = null; 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;
if (path.StartsWith("http")) {
byte[] src; byte[] src;
if (path.StartsWith("http")) {
#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 +200,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);

View file

@ -18,6 +18,24 @@ namespace MLEM.Textures {
return new TextureData(texture); return new TextureData(texture);
} }
/// <summary>
/// Creates and returns a copy of the given <paramref name="texture"/> with all colors converted to premultiplied alpha, which is the format that MonoGame's content pipeline loads textures in. This method uses <see cref="Color.FromNonPremultiplied(Microsoft.Xna.Framework.Vector4)"/> for all pixels in the <paramref name="texture"/>.
/// </summary>
/// <param name="texture">The texture of which to create a premultiplied copy.</param>
/// <returns>The premultiplied copy of the <paramref name="texture"/>.</returns>
public static Texture2D PremultipliedCopy(this Texture2D texture) {
var ret = new Texture2D(texture.GraphicsDevice, texture.Width, texture.Height);
using (var textureData = texture.GetTextureData()) {
using (var retData = ret.GetTextureData()) {
for (var x = 0; x < ret.Width; x++) {
for (var y = 0; y < ret.Height; y++)
retData[x, y] = Color.FromNonPremultiplied(textureData[x, y].ToVector4());
}
}
}
return ret;
}
/// <summary> /// <summary>
/// A struct that represents the data of a texture, accessed through <see cref="TextureExtensions.GetTextureData"/>. /// A struct that represents the data of a texture, accessed through <see cref="TextureExtensions.GetTextureData"/>.
/// </summary> /// </summary>

View file

@ -9,7 +9,7 @@ MLEM is platform-agnostic and multi-targets .NET Standard 2.0, .NET 8.0 and .NET
- Get prerelease builds on [BaGet](https://nuget.ellpeck.de/?q=mlem) - Get prerelease builds on [BaGet](https://nuget.ellpeck.de/?q=mlem)
- See the source code on [GitHub](https://github.com/Ellpeck/MLEM) - See the source code on [GitHub](https://github.com/Ellpeck/MLEM)
- See tutorials and API documentation on [the website](https://mlem.ellpeck.de/) - See tutorials and API documentation on [the website](https://mlem.ellpeck.de/)
- Check out [the demos](https://github.com/Ellpeck/MLEM/tree/main/Demos) on [Desktop](https://github.com/Ellpeck/MLEM/tree/main/Demos.DesktopGL) or [Android](https://github.com/Ellpeck/MLEM/tree/main/Demos.Android) - Check out [the demos](https://github.com/Ellpeck/MLEM/tree/main/Demos) on [Desktop](https://github.com/Ellpeck/MLEM/tree/main/Demos.DesktopGL), [Android](https://github.com/Ellpeck/MLEM/tree/main/Demos.Android) or [Web](https://mlem.ellpeck.de/demo)
- See [the changelog](https://mlem.ellpeck.de/CHANGELOG.html) for information on updates - See [the changelog](https://mlem.ellpeck.de/CHANGELOG.html) for information on updates
- Join [the Discord server](https://link.ellpeck.de/discordweb) to ask questions - Join [the Discord server](https://link.ellpeck.de/discordweb) to ask questions

View file

@ -2,7 +2,7 @@
#tool dotnet:?package=docfx&version=2.75.3 #tool dotnet:?package=docfx&version=2.75.3
// this is the upcoming version, for prereleases // this is the upcoming version, for prereleases
var version = Argument("version", "7.1.1"); var version = Argument("version", "7.1.2");
var target = Argument("target", "Default"); var target = Argument("target", "Default");
var gitRef = Argument("ref", "refs/heads/main"); var gitRef = Argument("ref", "refs/heads/main");
var buildNum = Argument("buildNum", ""); var buildNum = Argument("buildNum", "");
@ -83,6 +83,13 @@ Task("Document").Does(() => {
DocFxServe("Docs/_site"); DocFxServe("Docs/_site");
}); });
Task("PublishWeb").Does(() => {
DotNetPublish("Demos.Web/Demos.Web.KNI.csproj", new DotNetPublishSettings {
Configuration = config,
ArgumentCustomization = args => args.Append($"/p:Version={version}")
});
});
Task("Default").IsDependentOn("Pack"); Task("Default").IsDependentOn("Pack");
Task("Publish").IsDependentOn("Push"); Task("Publish").IsDependentOn("Push");