diff --git a/Directory.Packages.props b/Directory.Packages.props
index c310990720..8a9fdc3be5 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -42,11 +42,11 @@
-
-
+
+
-
\ No newline at end of file
+
diff --git a/Ryujinx.sln b/Ryujinx.sln
index b8304164d5..76ebd573f3 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -249,6 +251,10 @@ Global
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/ARMeilleure/Translation/TranslatorQueue.cs b/src/ARMeilleure/Translation/TranslatorQueue.cs
index cee2f9080d..831522bc14 100644
--- a/src/ARMeilleure/Translation/TranslatorQueue.cs
+++ b/src/ARMeilleure/Translation/TranslatorQueue.cs
@@ -80,7 +80,10 @@ namespace ARMeilleure.Translation
return true;
}
- Monitor.Wait(Sync);
+ if (!_disposed)
+ {
+ Monitor.Wait(Sync);
+ }
}
}
diff --git a/src/Ryujinx.Graphics.Device/DeviceState.cs b/src/Ryujinx.Graphics.Device/DeviceState.cs
index de8582a3b6..54178a4140 100644
--- a/src/Ryujinx.Graphics.Device/DeviceState.cs
+++ b/src/Ryujinx.Graphics.Device/DeviceState.cs
@@ -39,7 +39,10 @@ namespace Ryujinx.Graphics.Device
{
var field = fields[fieldIndex];
- int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
+ var currentFieldOffset = (int)Marshal.OffsetOf(field.Name);
+ var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf() : (int)Marshal.OffsetOf(fields[fieldIndex + 1].Name);
+
+ int sizeOfField = nextFieldOffset - currentFieldOffset;
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
{
diff --git a/src/Ryujinx.Graphics.Device/SizeCalculator.cs b/src/Ryujinx.Graphics.Device/SizeCalculator.cs
deleted file mode 100644
index 54820ec36f..0000000000
--- a/src/Ryujinx.Graphics.Device/SizeCalculator.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.Reflection;
-
-namespace Ryujinx.Graphics.Device
-{
- public static class SizeCalculator
- {
- public static int SizeOf(Type type)
- {
- // Is type a enum type?
- if (type.IsEnum)
- {
- type = type.GetEnumUnderlyingType();
- }
-
- // Is type a pointer type?
- if (type.IsPointer || type == typeof(IntPtr) || type == typeof(UIntPtr))
- {
- return IntPtr.Size;
- }
-
- // Is type a struct type?
- if (type.IsValueType && !type.IsPrimitive)
- {
- // Check if the struct has a explicit size, if so, return that.
- if (type.StructLayoutAttribute.Size != 0)
- {
- return type.StructLayoutAttribute.Size;
- }
-
- // Otherwise we calculate the sum of the sizes of all fields.
- int size = 0;
- var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
-
- for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
- {
- size += SizeOf(fields[fieldIndex].FieldType);
- }
-
- return size;
- }
-
- // Primitive types.
- return (Type.GetTypeCode(type)) switch
- {
- TypeCode.SByte => sizeof(sbyte),
- TypeCode.Byte => sizeof(byte),
- TypeCode.Int16 => sizeof(short),
- TypeCode.UInt16 => sizeof(ushort),
- TypeCode.Int32 => sizeof(int),
- TypeCode.UInt32 => sizeof(uint),
- TypeCode.Int64 => sizeof(long),
- TypeCode.UInt64 => sizeof(ulong),
- TypeCode.Char => sizeof(char),
- TypeCode.Single => sizeof(float),
- TypeCode.Double => sizeof(double),
- TypeCode.Decimal => sizeof(decimal),
- TypeCode.Boolean => sizeof(bool),
- _ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown."),
- };
- }
- }
-}
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
index e54855a8ff..effcb7bbb7 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
@@ -79,7 +79,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
var field = fields[fieldIndex];
- int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
+ var currentFieldOffset = (int)Marshal.OffsetOf(field.Name);
+ var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf() : (int)Marshal.OffsetOf(fields[fieldIndex + 1].Name);
+
+ int sizeOfField = nextFieldOffset - currentFieldOffset;
if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex))
{
diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs
index 8bb6516409..745335ac95 100644
--- a/src/Ryujinx.Gtk3/Program.cs
+++ b/src/Ryujinx.Gtk3/Program.cs
@@ -13,7 +13,6 @@ using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Common.SystemInfo;
using Ryujinx.UI.Widgets;
-using SixLabors.ImageSharp.Formats.Jpeg;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -162,12 +161,6 @@ namespace Ryujinx
});
};
- // Sets ImageSharp Jpeg Encoder Quality.
- SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
- {
- Quality = 100,
- });
-
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
diff --git a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj
index b4453f9d79..722d6080be 100644
--- a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj
+++ b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj
@@ -30,7 +30,6 @@
-
diff --git a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs
index 0e636792db..12139e87d9 100644
--- a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs
+++ b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs
@@ -13,16 +13,13 @@ using Ryujinx.Input.HLE;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Widgets;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Diagnostics;
using System.IO;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
-using Image = SixLabors.ImageSharp.Image;
using Key = Ryujinx.Input.Key;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
using Switch = Ryujinx.HLE.Switch;
@@ -404,23 +401,31 @@ namespace Ryujinx.UI
return;
}
- Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height)
- : Image.LoadPixelData(e.Data, e.Width, e.Height);
+ var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
+ using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
- if (e.FlipX)
+ Marshal.Copy(e.Data, 0, image.GetPixels(), e.Data.Length);
+ using var surface = SKSurface.Create(image.Info);
+ var canvas = surface.Canvas;
+
+ if (e.FlipX || e.FlipY)
{
- image.Mutate(x => x.Flip(FlipMode.Horizontal));
+ canvas.Clear(SKColors.Transparent);
+
+ float scaleX = e.FlipX ? -1 : 1;
+ float scaleY = e.FlipY ? -1 : 1;
+
+ var matrix = SKMatrix.CreateScale(scaleX, scaleY, image.Width / 2f, image.Height / 2f);
+
+ canvas.SetMatrix(matrix);
}
+ canvas.DrawBitmap(image, new SKPoint());
- if (e.FlipY)
- {
- image.Mutate(x => x.Flip(FlipMode.Vertical));
- }
-
- image.SaveAsPng(path, new PngEncoder()
- {
- ColorType = PngColorType.Rgb,
- });
+ surface.Flush();
+ using var snapshot = surface.Snapshot();
+ using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80);
+ using var file = File.OpenWrite(path);
+ encoded.SaveTo(file);
image.Dispose();
diff --git a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs
index d9ecd47b76..fcd960df0a 100644
--- a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs
+++ b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs
@@ -9,16 +9,13 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.FileSystem;
using Ryujinx.UI.Common.Configuration;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
-using Image = SixLabors.ImageSharp.Image;
+using System.Runtime.InteropServices;
namespace Ryujinx.UI.Windows
{
@@ -144,9 +141,11 @@ namespace Ryujinx.UI.Windows
stream.Position = 0;
- Image avatarImage = Image.LoadPixelData(DecompressYaz0(stream), 256, 256);
+ using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888));
+ var data = DecompressYaz0(stream);
+ Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length);
- avatarImage.SaveAsPng(streamPng);
+ avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80);
_avatarDict.Add(item.FullPath, streamPng.ToArray());
}
@@ -170,15 +169,23 @@ namespace Ryujinx.UI.Windows
{
using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
- Image avatarImage = Image.Load(data, new PngDecoder());
+ using var avatarImage = SKBitmap.Decode(data);
+ using var surface = SKSurface.Create(avatarImage.Info);
- avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
+ var background = new SKColor(
(byte)(_backgroundColor.Red * 255),
(byte)(_backgroundColor.Green * 255),
(byte)(_backgroundColor.Blue * 255),
(byte)(_backgroundColor.Alpha * 255)
- )));
- avatarImage.SaveAsJpeg(streamJpg);
+ );
+ var canvas = surface.Canvas;
+ canvas.Clear(background);
+ canvas.DrawBitmap(avatarImage, new SKPoint());
+
+ surface.Flush();
+ using var snapshot = surface.Snapshot();
+ using var encoded = snapshot.Encode(SKEncodedImageFormat.Jpeg, 80);
+ encoded.SaveTo(streamJpg);
return streamJpg.ToArray();
}
diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs
index d1e5fa9fc1..77afc5d1f1 100644
--- a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs
+++ b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs
@@ -4,15 +4,13 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Widgets;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
-using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.UI.Windows
{
@@ -177,13 +175,13 @@ namespace Ryujinx.UI.Windows
private void ProcessProfileImage(byte[] buffer)
{
- using Image image = Image.Load(buffer);
+ using var image = SKBitmap.Decode(buffer);
- image.Mutate(x => x.Resize(256, 256));
+ image.Resize(new SKImageInfo(256, 256), SKFilterQuality.High);
using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
- image.SaveAsJpeg(streamJpg);
+ image.Encode(streamJpg, SKEncodedImageFormat.Jpeg, 80);
_bufferImageProfile = streamJpg.ToArray();
}
diff --git a/src/Ryujinx.HLE.Generators/CodeGenerator.cs b/src/Ryujinx.HLE.Generators/CodeGenerator.cs
new file mode 100644
index 0000000000..7e4848ad39
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/CodeGenerator.cs
@@ -0,0 +1,63 @@
+using System.Text;
+
+namespace Ryujinx.HLE.Generators
+{
+ class CodeGenerator
+ {
+ private const int IndentLength = 4;
+
+ private readonly StringBuilder _sb;
+ private int _currentIndentCount;
+
+ public CodeGenerator()
+ {
+ _sb = new StringBuilder();
+ }
+
+ public void EnterScope(string header = null)
+ {
+ if (header != null)
+ {
+ AppendLine(header);
+ }
+
+ AppendLine("{");
+ IncreaseIndentation();
+ }
+
+ public void LeaveScope(string suffix = "")
+ {
+ DecreaseIndentation();
+ AppendLine($"}}{suffix}");
+ }
+
+ public void IncreaseIndentation()
+ {
+ _currentIndentCount++;
+ }
+
+ public void DecreaseIndentation()
+ {
+ if (_currentIndentCount - 1 >= 0)
+ {
+ _currentIndentCount--;
+ }
+ }
+
+ public void AppendLine()
+ {
+ _sb.AppendLine();
+ }
+
+ public void AppendLine(string text)
+ {
+ _sb.Append(' ', IndentLength * _currentIndentCount);
+ _sb.AppendLine(text);
+ }
+
+ public override string ToString()
+ {
+ return _sb.ToString();
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
new file mode 100644
index 0000000000..19fdbe1972
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
@@ -0,0 +1,76 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Linq;
+
+namespace Ryujinx.HLE.Generators
+{
+ [Generator]
+ public class IpcServiceGenerator : ISourceGenerator
+ {
+ public void Execute(GeneratorExecutionContext context)
+ {
+ var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
+ CodeGenerator generator = new CodeGenerator();
+
+ generator.AppendLine("using System;");
+ generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
+ generator.EnterScope($"partial class IUserInterface");
+
+ generator.EnterScope($"public IpcService? GetServiceInstance(Type type, ServiceCtx context, object? parameter = null)");
+ foreach (var className in syntaxReceiver.Types)
+ {
+ if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service"))))
+ continue;
+ var name = GetFullName(className, context).Replace("global::", "");
+ if (!name.StartsWith("Ryujinx.HLE.HOS.Services"))
+ continue;
+ var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax);
+
+ if (!constructors.Any(x => x.ParameterList.Parameters.Count >= 1))
+ continue;
+
+ if (constructors.Where(x => x.ParameterList.Parameters.Count >= 1).FirstOrDefault().ParameterList.Parameters[0].Type.ToString() == "ServiceCtx")
+ {
+ generator.EnterScope($"if (type == typeof({GetFullName(className, context)}))");
+ if (constructors.Any(x => x.ParameterList.Parameters.Count == 2))
+ {
+ var type = constructors.Where(x => x.ParameterList.Parameters.Count == 2).FirstOrDefault().ParameterList.Parameters[1].Type;
+ var model = context.Compilation.GetSemanticModel(type.SyntaxTree);
+ var typeSymbol = model.GetSymbolInfo(type).Symbol as INamedTypeSymbol;
+ var fullName = typeSymbol.ToString();
+ generator.EnterScope("if (parameter != null)");
+ generator.AppendLine($"return new {GetFullName(className, context)}(context, ({fullName})parameter);");
+ generator.LeaveScope();
+ }
+
+ if (constructors.Any(x => x.ParameterList.Parameters.Count == 1))
+ {
+ generator.AppendLine($"return new {GetFullName(className, context)}(context);");
+ }
+
+ generator.LeaveScope();
+ }
+ }
+
+ generator.AppendLine("return null;");
+ generator.LeaveScope();
+
+ generator.LeaveScope();
+ generator.LeaveScope();
+ context.AddSource($"IUserInterface.g.cs", generator.ToString());
+ }
+
+ private string GetFullName(ClassDeclarationSyntax syntaxNode, GeneratorExecutionContext context)
+ {
+ var typeSymbol = context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode);
+
+ return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ }
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(() => new ServiceSyntaxReceiver());
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj
new file mode 100644
index 0000000000..eeab9c0e97
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netstandard2.0
+ true
+ true
+ Generated
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs b/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs
new file mode 100644
index 0000000000..e4269cb9a4
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs
@@ -0,0 +1,24 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.Generators
+{
+ internal class ServiceSyntaxReceiver : ISyntaxReceiver
+ {
+ public HashSet Types = new HashSet();
+
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ if (syntaxNode is ClassDeclarationSyntax classDeclaration)
+ {
+ if (classDeclaration.BaseList == null)
+ {
+ return;
+ }
+
+ Types.Add(classDeclaration);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
index 30300f1b63..3c34d5c789 100644
--- a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
@@ -8,27 +8,24 @@ namespace Ryujinx.HLE.HOS.Applets
{
static class AppletManager
{
- private static readonly Dictionary _appletMapping;
-
- static AppletManager()
- {
- _appletMapping = new Dictionary
- {
- { AppletId.Error, typeof(ErrorApplet) },
- { AppletId.PlayerSelect, typeof(PlayerSelectApplet) },
- { AppletId.Controller, typeof(ControllerApplet) },
- { AppletId.SoftwareKeyboard, typeof(SoftwareKeyboardApplet) },
- { AppletId.LibAppletWeb, typeof(BrowserApplet) },
- { AppletId.LibAppletShop, typeof(BrowserApplet) },
- { AppletId.LibAppletOff, typeof(BrowserApplet) },
- };
- }
-
public static IApplet Create(AppletId applet, Horizon system)
{
- if (_appletMapping.TryGetValue(applet, out Type appletClass))
+ switch (applet)
{
- return (IApplet)Activator.CreateInstance(appletClass, system);
+ case AppletId.Controller:
+ return new ControllerApplet(system);
+ case AppletId.Error:
+ return new ErrorApplet(system);
+ case AppletId.PlayerSelect:
+ return new PlayerSelectApplet(system);
+ case AppletId.SoftwareKeyboard:
+ return new SoftwareKeyboardApplet(system);
+ case AppletId.LibAppletWeb:
+ return new BrowserApplet(system);
+ case AppletId.LibAppletShop:
+ return new BrowserApplet(system);
+ case AppletId.LibAppletOff:
+ return new BrowserApplet(system);
}
throw new NotImplementedException($"{applet} applet is not implemented.");
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
index 3f7516e6a7..239535ad5f 100644
--- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
@@ -112,11 +112,16 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
// Update the parameters that were provided.
_state.InputText = inputText ?? _state.InputText;
- _state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin);
- _state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd);
+ _state.CursorBegin = Math.Max(0, cursorBegin.GetValueOrDefault(_state.CursorBegin));
+ _state.CursorEnd = Math.Min(cursorEnd.GetValueOrDefault(_state.CursorEnd), _state.InputText.Length);
_state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode);
_state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled);
+ var begin = _state.CursorBegin;
+ var end = _state.CursorEnd;
+ _state.CursorBegin = Math.Min(begin, end);
+ _state.CursorEnd = Math.Max(begin, end);
+
// Reset the cursor blink.
_state.TextBoxBlinkCounter = 0;
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
index 9e48568e13..cc62eca1df 100644
--- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
@@ -1,14 +1,9 @@
using Ryujinx.HLE.UI;
using Ryujinx.Memory;
-using SixLabors.Fonts;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Drawing.Processing;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Diagnostics;
using System.IO;
-using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -29,38 +24,39 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
private readonly object _bufferLock = new();
private RenderingSurfaceInfo _surfaceInfo = null;
- private Image _surface = null;
+ private SKImageInfo _imageInfo;
+ private SKSurface _surface = null;
private byte[] _bufferData = null;
- private readonly Image _ryujinxLogo = null;
- private readonly Image _padAcceptIcon = null;
- private readonly Image _padCancelIcon = null;
- private readonly Image _keyModeIcon = null;
+ private readonly SKBitmap _ryujinxLogo = null;
+ private readonly SKBitmap _padAcceptIcon = null;
+ private readonly SKBitmap _padCancelIcon = null;
+ private readonly SKBitmap _keyModeIcon = null;
private readonly float _textBoxOutlineWidth;
private readonly float _padPressedPenWidth;
- private readonly Color _textNormalColor;
- private readonly Color _textSelectedColor;
- private readonly Color _textOverCursorColor;
+ private readonly SKColor _textNormalColor;
+ private readonly SKColor _textSelectedColor;
+ private readonly SKColor _textOverCursorColor;
- private readonly Brush _panelBrush;
- private readonly Brush _disabledBrush;
- private readonly Brush _cursorBrush;
- private readonly Brush _selectionBoxBrush;
+ private readonly SKPaint _panelBrush;
+ private readonly SKPaint _disabledBrush;
+ private readonly SKPaint _cursorBrush;
+ private readonly SKPaint _selectionBoxBrush;
- private readonly Pen _textBoxOutlinePen;
- private readonly Pen _cursorPen;
- private readonly Pen _selectionBoxPen;
- private readonly Pen _padPressedPen;
+ private readonly SKPaint _textBoxOutlinePen;
+ private readonly SKPaint _cursorPen;
+ private readonly SKPaint _selectionBoxPen;
+ private readonly SKPaint _padPressedPen;
private readonly int _inputTextFontSize;
- private Font _messageFont;
- private Font _inputTextFont;
- private Font _labelsTextFont;
+ private SKFont _messageFont;
+ private SKFont _inputTextFont;
+ private SKFont _labelsTextFont;
- private RectangleF _panelRectangle;
- private Point _logoPosition;
+ private SKRect _panelRectangle;
+ private SKPoint _logoPosition;
private float _messagePositionY;
public SoftwareKeyboardRendererBase(IHostUITheme uiTheme)
@@ -78,10 +74,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
_padCancelIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, padCancelIconPath, 0, 0);
_keyModeIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, keyModeIconPath, 0, 0);
- Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
- Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
- Color borderColor = ToColor(uiTheme.DefaultBorderColor);
- Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
+ var panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
+ var panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
+ var borderColor = ToColor(uiTheme.DefaultBorderColor);
+ var selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
_textNormalColor = ToColor(uiTheme.DefaultForegroundColor);
_textSelectedColor = ToColor(uiTheme.SelectionForegroundColor);
@@ -92,15 +88,29 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
_textBoxOutlineWidth = 2;
_padPressedPenWidth = 2;
- _panelBrush = new SolidBrush(panelColor);
- _disabledBrush = new SolidBrush(panelTransparentColor);
- _cursorBrush = new SolidBrush(_textNormalColor);
- _selectionBoxBrush = new SolidBrush(selectionBackgroundColor);
+ _panelBrush = new SKPaint()
+ {
+ Color = panelColor,
+ IsAntialias = true
+ };
+ _disabledBrush = new SKPaint()
+ {
+ Color = panelTransparentColor,
+ IsAntialias = true
+ };
+ _cursorBrush = new SKPaint() { Color = _textNormalColor, IsAntialias = true };
+ _selectionBoxBrush = new SKPaint() { Color = selectionBackgroundColor, IsAntialias = true };
- _textBoxOutlinePen = Pens.Solid(borderColor, _textBoxOutlineWidth);
- _cursorPen = Pens.Solid(_textNormalColor, cursorWidth);
- _selectionBoxPen = Pens.Solid(selectionBackgroundColor, cursorWidth);
- _padPressedPen = Pens.Solid(borderColor, _padPressedPenWidth);
+ _textBoxOutlinePen = new SKPaint()
+ {
+ Color = borderColor,
+ StrokeWidth = _textBoxOutlineWidth,
+ IsStroke = true,
+ IsAntialias = true
+ };
+ _cursorPen = new SKPaint() { Color = _textNormalColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true };
+ _selectionBoxPen = new SKPaint() { Color = selectionBackgroundColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true };
+ _padPressedPen = new SKPaint() { Color = borderColor, StrokeWidth = _padPressedPenWidth, IsStroke = true, IsAntialias = true };
_inputTextFontSize = 20;
@@ -123,9 +133,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
try
{
- _messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
- _inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
- _labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
+ using var typeface = SKTypeface.FromFamilyName(fontFamily, SKFontStyle.Normal);
+ _messageFont = new SKFont(typeface, 26);
+ _inputTextFont = new SKFont(typeface, _inputTextFontSize);
+ _labelsTextFont = new SKFont(typeface, 24);
return;
}
@@ -137,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!");
}
- private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
+ private static SKColor ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
{
var a = (byte)(color.A * 255);
var r = (byte)(color.R * 255);
@@ -151,34 +162,33 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
b = (byte)(255 - b);
}
- return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a));
+ return new SKColor(r, g, b, overrideAlpha.GetValueOrDefault(a));
}
- private static Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
+ private static SKBitmap LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
{
Stream resourceStream = assembly.GetManifestResourceStream(resourcePath);
return LoadResource(resourceStream, newWidth, newHeight);
}
- private static Image LoadResource(Stream resourceStream, int newWidth, int newHeight)
+ private static SKBitmap LoadResource(Stream resourceStream, int newWidth, int newHeight)
{
Debug.Assert(resourceStream != null);
- var image = Image.Load(resourceStream);
+ var bitmap = SKBitmap.Decode(resourceStream);
if (newHeight != 0 && newWidth != 0)
{
- image.Mutate(x => x.Resize(newWidth, newHeight, KnownResamplers.Lanczos3));
+ var resized = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKFilterQuality.High);
+ if (resized != null)
+ {
+ bitmap.Dispose();
+ bitmap = resized;
+ }
}
- return image;
- }
-
- private static void SetGraphicsOptions(IImageProcessingContext context)
- {
- context.GetGraphicsOptions().Antialias = true;
- context.GetDrawingOptions().GraphicsOptions.Antialias = true;
+ return bitmap;
}
private void DrawImmutableElements()
@@ -187,22 +197,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
return;
}
+ var canvas = _surface.Canvas;
- _surface.Mutate(context =>
- {
- SetGraphicsOptions(context);
+ canvas.Clear(SKColors.Transparent);
+ canvas.DrawRect(_panelRectangle, _panelBrush);
+ canvas.DrawBitmap(_ryujinxLogo, _logoPosition);
- context.Clear(Color.Transparent);
- context.Fill(_panelBrush, _panelRectangle);
- context.DrawImage(_ryujinxLogo, _logoPosition, 1);
+ float halfWidth = _panelRectangle.Width / 2;
+ float buttonsY = _panelRectangle.Top + 185;
- float halfWidth = _panelRectangle.Width / 2;
- float buttonsY = _panelRectangle.Y + 185;
+ SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY);
- PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
-
- DrawControllerToggle(context, disableButtonPosition);
- });
+ DrawControllerToggle(canvas, disableButtonPosition);
}
public void DrawMutableElements(SoftwareKeyboardUIState state)
@@ -212,40 +218,43 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
return;
}
- _surface.Mutate(context =>
+ using var paint = new SKPaint(_messageFont)
{
- var messageRectangle = MeasureString(MessageText, _messageFont);
- float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X;
- float messagePositionY = _messagePositionY - messageRectangle.Y;
- var messagePosition = new PointF(messagePositionX, messagePositionY);
- var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
+ Color = _textNormalColor,
+ IsAntialias = true
+ };
- SetGraphicsOptions(context);
+ var canvas = _surface.Canvas;
+ var messageRectangle = MeasureString(MessageText, paint);
+ float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.Left;
+ float messagePositionY = _messagePositionY - messageRectangle.Top;
+ var messagePosition = new SKPoint(messagePositionX, messagePositionY);
+ var messageBoundRectangle = SKRect.Create(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
- context.Fill(_panelBrush, messageBoundRectangle);
+ canvas.DrawRect(messageBoundRectangle, _panelBrush);
- context.DrawText(MessageText, _messageFont, _textNormalColor, messagePosition);
+ canvas.DrawText(MessageText, messagePosition.X, messagePosition.Y + _messageFont.Metrics.XHeight + _messageFont.Metrics.Descent, paint);
- if (!state.TypingEnabled)
- {
- // Just draw a semi-transparent rectangle on top to fade the component with the background.
- // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
+ if (!state.TypingEnabled)
+ {
+ // Just draw a semi-transparent rectangle on top to fade the component with the background.
+ // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- context.Fill(_disabledBrush, messageBoundRectangle);
- }
+ canvas.DrawRect(messageBoundRectangle, _disabledBrush);
+ }
- DrawTextBox(context, state);
+ DrawTextBox(canvas, state);
- float halfWidth = _panelRectangle.Width / 2;
- float buttonsY = _panelRectangle.Y + 185;
+ float halfWidth = _panelRectangle.Width / 2;
+ float buttonsY = _panelRectangle.Top + 185;
- PointF acceptButtonPosition = new(halfWidth - 180, buttonsY);
- PointF cancelButtonPosition = new(halfWidth, buttonsY);
- PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
+ SKPoint acceptButtonPosition = new(halfWidth - 180, buttonsY);
+ SKPoint cancelButtonPosition = new(halfWidth, buttonsY);
+ SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY);
+
+ DrawPadButton(canvas, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
+ DrawPadButton(canvas, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
- DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
- DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
- });
}
public void CreateSurface(RenderingSurfaceInfo surfaceInfo)
@@ -268,7 +277,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
Debug.Assert(_surfaceInfo.Height <= totalHeight);
Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size);
- _surface = new Image((int)totalWidth, (int)totalHeight);
+ _imageInfo = new SKImageInfo((int)totalWidth, (int)totalHeight, SKColorType.Rgba8888);
+ _surface = SKSurface.Create(_imageInfo);
ComputeConstants();
DrawImmutableElements();
@@ -282,76 +292,81 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
int panelHeight = 240;
int panelPositionY = totalHeight - panelHeight;
- _panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight);
+ _panelRectangle = SKRect.Create(0, panelPositionY, totalWidth, panelHeight);
_messagePositionY = panelPositionY + 60;
int logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2;
int logoPositionY = panelPositionY + 18;
- _logoPosition = new Point(logoPositionX, logoPositionY);
+ _logoPosition = new SKPoint(logoPositionX, logoPositionY);
}
- private static RectangleF MeasureString(string text, Font font)
+ private static SKRect MeasureString(string text, SKPaint paint)
{
- TextOptions options = new(font);
+ SKRect bounds = SKRect.Empty;
if (text == "")
{
- FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options);
-
- return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height);
+ paint.MeasureText(" ", ref bounds);
+ }
+ else
+ {
+ paint.MeasureText(text, ref bounds);
}
- FontRectangle rectangle = TextMeasurer.MeasureSize(text, options);
-
- return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ return bounds;
}
- private static RectangleF MeasureString(ReadOnlySpan text, Font font)
+ private static SKRect MeasureString(ReadOnlySpan text, SKPaint paint)
{
- TextOptions options = new(font);
+ SKRect bounds = SKRect.Empty;
if (text == "")
{
- FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options);
- return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height);
+ paint.MeasureText(" ", ref bounds);
+ }
+ else
+ {
+ paint.MeasureText(text, ref bounds);
}
- FontRectangle rectangle = TextMeasurer.MeasureSize(text, options);
-
- return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ return bounds;
}
- private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUIState state)
+ private void DrawTextBox(SKCanvas canvas, SoftwareKeyboardUIState state)
{
- var inputTextRectangle = MeasureString(state.InputText, _inputTextFont);
+ using var textPaint = new SKPaint(_labelsTextFont)
+ {
+ IsAntialias = true,
+ Color = _textNormalColor
+ };
+ var inputTextRectangle = MeasureString(state.InputText, textPaint);
- float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8));
+ float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.Left + 8));
float boxHeight = 32;
- float boxY = _panelRectangle.Y + 110;
+ float boxY = _panelRectangle.Top + 110;
float boxX = (int)((_panelRectangle.Width - boxWidth) / 2);
- RectangleF boxRectangle = new(boxX, boxY, boxWidth, boxHeight);
+ SKRect boxRectangle = SKRect.Create(boxX, boxY, boxWidth, boxHeight);
- RectangleF boundRectangle = new(_panelRectangle.X, boxY - _textBoxOutlineWidth,
+ SKRect boundRectangle = SKRect.Create(_panelRectangle.Left, boxY - _textBoxOutlineWidth,
_panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth);
- context.Fill(_panelBrush, boundRectangle);
+ canvas.DrawRect(boundRectangle, _panelBrush);
- context.Draw(_textBoxOutlinePen, boxRectangle);
+ canvas.DrawRect(boxRectangle, _textBoxOutlinePen);
- float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X;
+ float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.Left;
float inputTextY = boxY + 5;
- var inputTextPosition = new PointF(inputTextX, inputTextY);
-
- context.DrawText(state.InputText, _inputTextFont, _textNormalColor, inputTextPosition);
+ var inputTextPosition = new SKPoint(inputTextX, inputTextY);
+ canvas.DrawText(state.InputText, inputTextPosition.X, inputTextPosition.Y + (_labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent), textPaint);
// Draw the cursor on top of the text and redraw the text with a different color if necessary.
- Color cursorTextColor;
- Brush cursorBrush;
- Pen cursorPen;
+ SKColor cursorTextColor;
+ SKPaint cursorBrush;
+ SKPaint cursorPen;
float cursorPositionYTop = inputTextY + 1;
float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1;
@@ -371,12 +386,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
ReadOnlySpan textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin);
ReadOnlySpan textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd);
- var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont);
- var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont);
+ var selectionBeginRectangle = MeasureString(textUntilBegin, textPaint);
+ var selectionEndRectangle = MeasureString(textUntilEnd, textPaint);
cursorVisible = true;
- cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X;
- cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X;
+ cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.Left;
+ cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.Left;
}
else
{
@@ -390,10 +405,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin);
ReadOnlySpan textUntilCursor = state.InputText.AsSpan(0, cursorBegin);
- var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
+ var cursorTextRectangle = MeasureString(textUntilCursor, textPaint);
cursorVisible = true;
- cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
+ cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left;
if (state.OverwriteMode)
{
@@ -402,8 +417,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
if (state.CursorBegin < state.InputText.Length)
{
textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1);
- cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
- cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
+ cursorTextRectangle = MeasureString(textUntilCursor, textPaint);
+ cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left;
}
else
{
@@ -430,29 +445,32 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
if (cursorWidth == 0)
{
- PointF[] points = {
- new PointF(cursorPositionXLeft, cursorPositionYTop),
- new PointF(cursorPositionXLeft, cursorPositionYBottom),
- };
-
- context.DrawLine(cursorPen, points);
+ canvas.DrawLine(new SKPoint(cursorPositionXLeft, cursorPositionYTop),
+ new SKPoint(cursorPositionXLeft, cursorPositionYBottom),
+ cursorPen);
}
else
{
- var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
+ var cursorRectangle = SKRect.Create(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
- context.Draw(cursorPen, cursorRectangle);
- context.Fill(cursorBrush, cursorRectangle);
+ canvas.DrawRect(cursorRectangle, cursorPen);
+ canvas.DrawRect(cursorRectangle, cursorBrush);
- Image textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height);
- textOverCursor.Mutate(context =>
+ using var textOverCursor = SKSurface.Create(new SKImageInfo((int)cursorRectangle.Width, (int)cursorRectangle.Height, SKColorType.Rgba8888));
+ var textOverCanvas = textOverCursor.Canvas;
+ var textRelativePosition = new SKPoint(inputTextPosition.X - cursorRectangle.Left, inputTextPosition.Y - cursorRectangle.Top);
+
+ using var cursorPaint = new SKPaint(_inputTextFont)
{
- var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y);
- context.DrawText(state.InputText, _inputTextFont, cursorTextColor, textRelativePosition);
- });
+ Color = cursorTextColor,
+ IsAntialias = true
+ };
- var cursorPosition = new Point((int)cursorRectangle.X, (int)cursorRectangle.Y);
- context.DrawImage(textOverCursor, cursorPosition, 1);
+ textOverCanvas.DrawText(state.InputText, textRelativePosition.X, textRelativePosition.Y + _inputTextFont.Metrics.XHeight + _inputTextFont.Metrics.Descent, cursorPaint);
+
+ var cursorPosition = new SKPoint((int)cursorRectangle.Left, (int)cursorRectangle.Top);
+ textOverCursor.Flush();
+ canvas.DrawSurface(textOverCursor, cursorPosition);
}
}
else if (!state.TypingEnabled)
@@ -460,11 +478,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Just draw a semi-transparent rectangle on top to fade the component with the background.
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- context.Fill(_disabledBrush, boundRectangle);
+ canvas.DrawRect(boundRectangle, _disabledBrush);
}
}
- private void DrawPadButton(IImageProcessingContext context, PointF point, Image icon, string label, bool pressed, bool enabled)
+ private void DrawPadButton(SKCanvas canvas, SKPoint point, SKBitmap icon, string label, bool pressed, bool enabled)
{
// Use relative positions so we can center the entire drawing later.
@@ -473,12 +491,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
float iconWidth = icon.Width;
float iconHeight = icon.Height;
- var labelRectangle = MeasureString(label, _labelsTextFont);
+ using var paint = new SKPaint(_labelsTextFont)
+ {
+ Color = _textNormalColor,
+ IsAntialias = true
+ };
- float labelPositionX = iconWidth + 8 - labelRectangle.X;
+ var labelRectangle = MeasureString(label, paint);
+
+ float labelPositionX = iconWidth + 8 - labelRectangle.Left;
float labelPositionY = 3;
- float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X;
+ float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.Left;
float fullHeight = iconHeight;
// Convert all relative positions into absolute.
@@ -489,24 +513,24 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
iconX += originX;
iconY += originY;
- var iconPosition = new Point((int)iconX, (int)iconY);
- var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
+ var iconPosition = new SKPoint((int)iconX, (int)iconY);
+ var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY);
- var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
+ var selectedRectangle = SKRect.Create(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth);
- var boundRectangle = new RectangleF(originX, originY, fullWidth, fullHeight);
+ var boundRectangle = SKRect.Create(originX, originY, fullWidth, fullHeight);
boundRectangle.Inflate(4 * _padPressedPenWidth, 4 * _padPressedPenWidth);
- context.Fill(_panelBrush, boundRectangle);
- context.DrawImage(icon, iconPosition, 1);
- context.DrawText(label, _labelsTextFont, _textNormalColor, labelPosition);
+ canvas.DrawRect(boundRectangle, _panelBrush);
+ canvas.DrawBitmap(icon, iconPosition);
+ canvas.DrawText(label, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent, paint);
if (enabled)
{
if (pressed)
{
- context.Draw(_padPressedPen, selectedRectangle);
+ canvas.DrawRect(selectedRectangle, _padPressedPen);
}
}
else
@@ -514,21 +538,26 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Just draw a semi-transparent rectangle on top to fade the component with the background.
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- context.Fill(_disabledBrush, boundRectangle);
+ canvas.DrawRect(boundRectangle, _disabledBrush);
}
}
- private void DrawControllerToggle(IImageProcessingContext context, PointF point)
+ private void DrawControllerToggle(SKCanvas canvas, SKPoint point)
{
- var labelRectangle = MeasureString(ControllerToggleText, _labelsTextFont);
+ using var paint = new SKPaint(_labelsTextFont)
+ {
+ IsAntialias = true,
+ Color = _textNormalColor
+ };
+ var labelRectangle = MeasureString(ControllerToggleText, paint);
// Use relative positions so we can center the entire drawing later.
float keyWidth = _keyModeIcon.Width;
float keyHeight = _keyModeIcon.Height;
- float labelPositionX = keyWidth + 8 - labelRectangle.X;
- float labelPositionY = -labelRectangle.Y - 1;
+ float labelPositionX = keyWidth + 8 - labelRectangle.Left;
+ float labelPositionY = -labelRectangle.Top - 1;
float keyX = 0;
float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2);
@@ -544,14 +573,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
keyX += originX;
keyY += originY;
- var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
- var overlayPosition = new Point((int)keyX, (int)keyY);
+ var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY);
+ var overlayPosition = new SKPoint((int)keyX, (int)keyY);
- context.DrawImage(_keyModeIcon, overlayPosition, 1);
- context.DrawText(ControllerToggleText, _labelsTextFont, _textNormalColor, labelPosition);
+ canvas.DrawBitmap(_keyModeIcon, overlayPosition);
+ canvas.DrawText(ControllerToggleText, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight, paint);
}
- public void CopyImageToBuffer()
+ public unsafe void CopyImageToBuffer()
{
lock (_bufferLock)
{
@@ -561,21 +590,20 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
}
// Convert the pixel format used in the image to the one used in the Switch surface.
+ _surface.Flush();
- if (!_surface.DangerousTryGetSinglePixelMemory(out Memory pixels))
+ var buffer = new byte[_imageInfo.BytesSize];
+ fixed (byte* bufferPtr = buffer)
{
- return;
+ if (!_surface.ReadPixels(_imageInfo, (nint)bufferPtr, _imageInfo.RowBytes, 0, 0))
+ {
+ return;
+ }
}
- _bufferData = MemoryMarshal.AsBytes(pixels.Span).ToArray();
- Span dataConvert = MemoryMarshal.Cast(_bufferData);
+ _bufferData = buffer;
- Debug.Assert(_bufferData.Length == _surfaceInfo.Size);
-
- for (int i = 0; i < dataConvert.Length; i++)
- {
- dataConvert[i] = BitOperations.RotateRight(dataConvert[i], 8);
- }
+ Debug.Assert(buffer.Length == _surfaceInfo.Size);
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
index 91a8958e6c..bf0c7e9dc6 100644
--- a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
@@ -1,10 +1,10 @@
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Caps.Types;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
+using SkiaSharp;
using System;
using System.IO;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace Ryujinx.HLE.HOS.Services.Caps
@@ -118,7 +118,11 @@ namespace Ryujinx.HLE.HOS.Services.Caps
}
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
- Image.LoadPixelData(screenshotData, 1280, 720).SaveAsJpegAsync(filePath);
+ using var bitmap = new SKBitmap(new SKImageInfo(1280, 720, SKColorType.Rgba8888));
+ Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), screenshotData.Length);
+ using var data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
+ using var file = File.OpenWrite(filePath);
+ data.SaveTo(file);
return ResultCode.Success;
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index 3dc82035fd..7a90c664e3 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Ipc;
+using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.Horizon.Common;
using System;
using System.Collections.Generic;
@@ -12,7 +13,7 @@ using System.Text;
namespace Ryujinx.HLE.HOS.Services.Sm
{
- class IUserInterface : IpcService
+ partial class IUserInterface : IpcService
{
private static readonly Dictionary _services;
@@ -95,9 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name);
- IpcService service = serviceAttribute.Parameter != null
- ? (IpcService)Activator.CreateInstance(type, context, serviceAttribute.Parameter)
- : (IpcService)Activator.CreateInstance(type, context);
+ IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);
service.TrySetServer(_commonServer);
service.Server.AddSessionObj(session.ServerSession, service);
diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
index 0fcf9e4b57..a7bb3cd7f6 100644
--- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -2,6 +2,7 @@
net8.0
+ true
@@ -11,6 +12,7 @@
+
@@ -24,8 +26,8 @@
-
-
+
+
diff --git a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs
index 58bdc90e6a..1849f40cbb 100644
--- a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs
+++ b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs
@@ -1,10 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using ShellLink;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Collections.Generic;
using System.IO;
@@ -21,8 +18,8 @@ namespace Ryujinx.UI.Common.Helper
iconPath += ".ico";
MemoryStream iconDataStream = new(iconData);
- var image = Image.Load(iconDataStream);
- image.Mutate(x => x.Resize(128, 128));
+ using var image = SKBitmap.Decode(iconDataStream);
+ image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
SaveBitmapAsIcon(image, iconPath);
var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0);
@@ -37,8 +34,10 @@ namespace Ryujinx.UI.Common.Helper
var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop");
iconPath += ".png";
- var image = Image.Load(iconData);
- image.SaveAsPng(iconPath);
+ var image = SKBitmap.Decode(iconData);
+ using var data = image.Encode(SKEncodedImageFormat.Png, 100);
+ using var file = File.OpenWrite(iconPath);
+ data.SaveTo(file);
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}");
@@ -78,8 +77,10 @@ namespace Ryujinx.UI.Common.Helper
}
const string IconName = "icon.png";
- var image = Image.Load(iconData);
- image.SaveAsPng(Path.Combine(resourceFolderPath, IconName));
+ var image = SKBitmap.Decode(iconData);
+ using var data = image.Encode(SKEncodedImageFormat.Png, 100);
+ using var file = File.OpenWrite(Path.Combine(resourceFolderPath, IconName));
+ data.SaveTo(file);
// plist file
using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist"));
@@ -148,7 +149,7 @@ namespace Ryujinx.UI.Common.Helper
/// The source bitmap image that will be saved as an .ico file
/// The location that the new .ico file will be saved too (Make sure to include '.ico' in the path).
[SupportedOSPlatform("windows")]
- private static void SaveBitmapAsIcon(Image source, string filePath)
+ private static void SaveBitmapAsIcon(SKBitmap source, string filePath)
{
// Code Modified From https://stackoverflow.com/a/11448060/368354 by Benlitz
byte[] header = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 22, 0, 0, 0 };
@@ -156,13 +157,16 @@ namespace Ryujinx.UI.Common.Helper
fs.Write(header);
// Writing actual data
- source.Save(fs, PngFormat.Instance);
+ using var data = source.Encode(SKEncodedImageFormat.Png, 100);
+ data.SaveTo(fs);
// Getting data length (file length minus header)
long dataLength = fs.Length - header.Length;
// Write it in the correct place
fs.Seek(14, SeekOrigin.Begin);
fs.WriteByte((byte)dataLength);
fs.WriteByte((byte)(dataLength >> 8));
+ fs.WriteByte((byte)(dataLength >> 16));
+ fs.WriteByte((byte)(dataLength >> 24));
}
}
}
diff --git a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs
index 531d006115..0e7cfb8e6c 100644
--- a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs
+++ b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs
@@ -41,17 +41,12 @@ namespace Ryujinx.Ava.UI.Applet
private void TextChanged(string text)
{
- TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true);
+ TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false);
}
private void SelectionChanged(int selection)
{
- if (_hiddenTextBox.SelectionEnd < _hiddenTextBox.SelectionStart)
- {
- _hiddenTextBox.SelectionStart = _hiddenTextBox.SelectionEnd;
- }
-
- TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true);
+ TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false);
}
private void AvaloniaDynamicTextInputHandler_TextInput(object sender, string text)
diff --git a/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs b/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs
index a055f33538..dd736037ee 100644
--- a/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs
+++ b/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs
@@ -1,11 +1,14 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
+using System;
namespace Ryujinx.Ava.UI.Helpers
{
public class OffscreenTextBox : TextBox
{
+ protected override Type StyleKeyOverride => typeof(TextBox);
+
public static RoutedEvent GetKeyDownRoutedEvent()
{
return KeyDownEvent;
diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml b/src/Ryujinx/UI/Windows/MainWindow.axaml
index 6c2042f93c..3a2e02c260 100644
--- a/src/Ryujinx/UI/Windows/MainWindow.axaml
+++ b/src/Ryujinx/UI/Windows/MainWindow.axaml
@@ -42,12 +42,10 @@
-
-
+