aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsunshineinabox <aqemail@gmail.com>2024-07-14 01:16:14 -0700
committerGitHub <noreply@github.com>2024-07-14 08:16:14 +0000
commit595e514f1804ee2da31ff1b24c2facfc120386d1 (patch)
tree6fb537032382633a5d19483e8d44df2aac4a9d6d
parent07435ad844bb615348fa980bb048a89298b6a652 (diff)
Use SkiaSharp for Avalonia in place of ImageSharp (#6269)
* Rebased Transformation all at once Use SkiaSharp instead of ImageSharp * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Change back unintentionally changed comment --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> Co-authored-by: Emmanuel Hansen <emmausssss@gmail.com>
-rw-r--r--src/Ryujinx/AppHost.cs51
-rw-r--r--src/Ryujinx/Ryujinx.csproj1
-rw-r--r--src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs15
-rw-r--r--src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs11
-rw-r--r--src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs34
-rw-r--r--src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs18
-rw-r--r--src/Ryujinx/UI/Windows/IconColorPicker.cs45
7 files changed, 104 insertions, 71 deletions
diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs
index d405f320..7004908a 100644
--- a/src/Ryujinx/AppHost.cs
+++ b/src/Ryujinx/AppHost.cs
@@ -40,20 +40,17 @@ using Ryujinx.UI.Common;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
using Silk.NET.Vulkan;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using SPB.Graphics.Vulkan;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
-using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager;
using IRenderer = Ryujinx.Graphics.GAL.IRenderer;
using Key = Ryujinx.Input.Key;
@@ -366,25 +363,33 @@ namespace Ryujinx.Ava
return;
}
- Image image = e.IsBgra ? Image.LoadPixelData<Bgra32>(e.Data, e.Width, e.Height)
- : Image.LoadPixelData<Rgba32>(e.Data, e.Width, e.Height);
+ var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
+ using var bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
- if (e.FlipX)
- {
- image.Mutate(x => x.Flip(FlipMode.Horizontal));
- }
+ Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length);
- if (e.FlipY)
- {
- image.Mutate(x => x.Flip(FlipMode.Vertical));
- }
+ SKBitmap bitmapToSave = null;
- image.SaveAsPng(path, new PngEncoder
+ if (e.FlipX || e.FlipY)
{
- ColorType = PngColorType.Rgb,
- });
+ bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
+
+ using var canvas = new SKCanvas(bitmapToSave);
+
+ canvas.Clear(SKColors.Transparent);
+
+ float scaleX = e.FlipX ? -1 : 1;
+ float scaleY = e.FlipY ? -1 : 1;
+
+ var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
- image.Dispose();
+ canvas.SetMatrix(matrix);
+
+ canvas.DrawBitmap(bitmap, new SKPoint(e.FlipX ? -bitmap.Width : 0, e.FlipY ? -bitmap.Height : 0));
+ }
+
+ SaveBitmapAsPng(bitmapToSave ?? bitmap, path);
+ bitmapToSave?.Dispose();
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot");
}
@@ -396,6 +401,14 @@ namespace Ryujinx.Ava
}
}
+ private void SaveBitmapAsPng(SKBitmap bitmap, string path)
+ {
+ using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100);
+ using var stream = File.OpenWrite(path);
+
+ data.SaveTo(stream);
+ }
+
public void Start()
{
if (OperatingSystem.IsWindows())
diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj
index a43f5006..6718b7fc 100644
--- a/src/Ryujinx/Ryujinx.csproj
+++ b/src/Ryujinx/Ryujinx.csproj
@@ -54,7 +54,6 @@
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
<PackageReference Include="SPB" />
<PackageReference Include="SharpZipLib" />
- <PackageReference Include="SixLabors.ImageSharp" />
</ItemGroup>
<ItemGroup>
diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
index 549eebf1..b47cc4b7 100644
--- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
@@ -32,7 +32,7 @@ using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
-using SixLabors.ImageSharp.PixelFormats;
+using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -40,7 +40,6 @@ using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using Image = SixLabors.ImageSharp.Image;
using Key = Ryujinx.Input.Key;
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
@@ -1164,17 +1163,17 @@ namespace Ryujinx.Ava.UI.ViewModels
private void PrepareLoadScreen()
{
using MemoryStream stream = new(SelectedIcon);
- using var gameIconBmp = Image.Load<Bgra32>(stream);
+ using var gameIconBmp = SKBitmap.Decode(stream);
- var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>();
+ var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp);
const float ColorMultiple = 0.5f;
- Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B);
+ Color progressFgColor = Color.FromRgb(dominantColor.Red, dominantColor.Green, dominantColor.Blue);
Color progressBgColor = Color.FromRgb(
- (byte)(dominantColor.R * ColorMultiple),
- (byte)(dominantColor.G * ColorMultiple),
- (byte)(dominantColor.B * ColorMultiple));
+ (byte)(dominantColor.Red * ColorMultiple),
+ (byte)(dominantColor.Green * ColorMultiple),
+ (byte)(dominantColor.Blue * ColorMultiple));
ProgressBarForegroundColor = new SolidColorBrush(progressFgColor);
ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor);
diff --git a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs
index 12adfe94..b07bf78b 100644
--- a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs
@@ -9,14 +9,14 @@ using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
+using SkiaSharp;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using Color = Avalonia.Media.Color;
+using Image = SkiaSharp.SKImage;
namespace Ryujinx.Ava.UI.ViewModels
{
@@ -130,9 +130,12 @@ namespace Ryujinx.Ava.UI.ViewModels
stream.Position = 0;
- Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
+ Image avatarImage = Image.FromPixelCopy(new SKImageInfo(256, 256, SKColorType.Rgba8888, SKAlphaType.Premul), DecompressYaz0(stream));
- avatarImage.SaveAsPng(streamPng);
+ using (SKData data = avatarImage.Encode(SKEncodedImageFormat.Png, 100))
+ {
+ data.SaveTo(streamPng);
+ }
_avatarStore.Add(item.FullPath, streamPng.ToArray());
}
diff --git a/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs
index b6376866..064b5e90 100644
--- a/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs
+++ b/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs
@@ -6,12 +6,8 @@ using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System.IO;
-using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.Ava.UI.Views.User
{
@@ -70,15 +66,25 @@ namespace Ryujinx.Ava.UI.Views.User
{
if (ViewModel.SelectedImage != null)
{
- MemoryStream streamJpg = new();
- Image avatarImage = Image.Load(ViewModel.SelectedImage, new PngDecoder());
-
- avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
- ViewModel.BackgroundColor.R,
- ViewModel.BackgroundColor.G,
- ViewModel.BackgroundColor.B,
- ViewModel.BackgroundColor.A)));
- avatarImage.SaveAsJpeg(streamJpg);
+ using var streamJpg = new MemoryStream();
+ using var bitmap = SKBitmap.Decode(ViewModel.SelectedImage);
+ using var newBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
+
+ using (var canvas = new SKCanvas(newBitmap))
+ {
+ canvas.Clear(new SKColor(
+ ViewModel.BackgroundColor.R,
+ ViewModel.BackgroundColor.G,
+ ViewModel.BackgroundColor.B,
+ ViewModel.BackgroundColor.A));
+ canvas.DrawBitmap(bitmap, 0, 0);
+ }
+
+ using (var image = SKImage.FromBitmap(newBitmap))
+ using (var dataJpeg = image.Encode(SKEncodedImageFormat.Jpeg, 100))
+ {
+ dataJpeg.SaveTo(streamJpg);
+ }
_profile.Image = streamJpg.ToArray();
diff --git a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs
index fabfaa4e..b4f23b5b 100644
--- a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs
+++ b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs
@@ -9,11 +9,9 @@ using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System.Collections.Generic;
using System.IO;
-using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.Ava.UI.Views.User
{
@@ -102,13 +100,19 @@ namespace Ryujinx.Ava.UI.Views.User
private static byte[] ProcessProfileImage(byte[] buffer)
{
- using Image image = Image.Load(buffer);
+ using var bitmap = SKBitmap.Decode(buffer);
- image.Mutate(x => x.Resize(256, 256));
+ var resizedBitmap = bitmap.Resize(new SKImageInfo(256, 256), SKFilterQuality.High);
- using MemoryStream streamJpg = new();
+ using var streamJpg = new MemoryStream();
- image.SaveAsJpeg(streamJpg);
+ if (resizedBitmap != null)
+ {
+ using var image = SKImage.FromBitmap(resizedBitmap);
+ using var dataJpeg = image.Encode(SKEncodedImageFormat.Jpeg, 100);
+
+ dataJpeg.SaveTo(streamJpg);
+ }
return streamJpg.ToArray();
}
diff --git a/src/Ryujinx/UI/Windows/IconColorPicker.cs b/src/Ryujinx/UI/Windows/IconColorPicker.cs
index 72660351..dd6a55d4 100644
--- a/src/Ryujinx/UI/Windows/IconColorPicker.cs
+++ b/src/Ryujinx/UI/Windows/IconColorPicker.cs
@@ -1,5 +1,4 @@
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
+using SkiaSharp;
using System;
using System.Collections.Generic;
@@ -36,35 +35,34 @@ namespace Ryujinx.Ava.UI.Windows
}
}
- public static Color GetFilteredColor(Image<Bgra32> image)
+ public static SKColor GetFilteredColor(SKBitmap image)
{
- var color = GetColor(image).ToPixel<Bgra32>();
+ var color = GetColor(image);
+
// We don't want colors that are too dark.
// If the color is too dark, make it brighter by reducing the range
// and adding a constant color.
- int luminosity = GetColorApproximateLuminosity(color.R, color.G, color.B);
+ int luminosity = GetColorApproximateLuminosity(color.Red, color.Green, color.Blue);
if (luminosity < CutOffLuminosity)
{
- color = Color.FromRgb(
- (byte)Math.Min(CutOffLuminosity + color.R, byte.MaxValue),
- (byte)Math.Min(CutOffLuminosity + color.G, byte.MaxValue),
- (byte)Math.Min(CutOffLuminosity + color.B, byte.MaxValue));
+ color = new SKColor(
+ (byte)Math.Min(CutOffLuminosity + color.Red, byte.MaxValue),
+ (byte)Math.Min(CutOffLuminosity + color.Green, byte.MaxValue),
+ (byte)Math.Min(CutOffLuminosity + color.Blue, byte.MaxValue));
}
return color;
}
- public static Color GetColor(Image<Bgra32> image)
+ public static SKColor GetColor(SKBitmap image)
{
var colors = new PaletteColor[TotalColors];
-
var dominantColorBin = new Dictionary<int, int>();
var buffer = GetBuffer(image);
int w = image.Width;
-
int w8 = w << 8;
int h8 = image.Height << 8;
@@ -84,9 +82,10 @@ namespace Ryujinx.Ava.UI.Windows
{
int offset = x + yOffset;
- byte cb = buffer[offset].B;
- byte cg = buffer[offset].G;
- byte cr = buffer[offset].R;
+ SKColor pixel = buffer[offset];
+ byte cr = pixel.Red;
+ byte cg = pixel.Green;
+ byte cb = pixel.Blue;
var qck = GetQuantizedColorKey(cr, cg, cb);
@@ -122,12 +121,22 @@ namespace Ryujinx.Ava.UI.Windows
}
}
- return Color.FromRgb(bestCandidate.R, bestCandidate.G, bestCandidate.B);
+ return new SKColor(bestCandidate.R, bestCandidate.G, bestCandidate.B);
}
- public static Bgra32[] GetBuffer(Image<Bgra32> image)
+ public static SKColor[] GetBuffer(SKBitmap image)
{
- return image.DangerousTryGetSinglePixelMemory(out var data) ? data.ToArray() : Array.Empty<Bgra32>();
+ var pixels = new SKColor[image.Width * image.Height];
+
+ for (int y = 0; y < image.Height; y++)
+ {
+ for (int x = 0; x < image.Width; x++)
+ {
+ pixels[x + y * image.Width] = image.GetPixel(x, y);
+ }
+ }
+
+ return pixels;
}
private static int GetColorScore(Dictionary<int, int> dominantColorBin, int maxHitCount, PaletteColor color)