Просмотр исходного кода

Replace ImageSharp with SkiaSharp everywhere (#7030)

* replace ImageSharp with SkiaSharp for inline keyboard applet rendering

* fix avalonia inline keyboard input

* remove image sharp from gtk3 project

* add skiasharp linux assets

* fix whitespace

* fix format

* fix ico image offset when saving shortcut to windows
Emmanuel Hansen 1 год назад
Родитель
Сommit
e0acde04bb

+ 3 - 3
Directory.Packages.props

@@ -42,11 +42,11 @@
     <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
     <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
     <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
-    <PackageVersion Include="SixLabors.ImageSharp" Version="2.1.9" />
-    <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
+    <PackageVersion Include="SkiaSharp" Version="2.88.7" />
+    <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
     <PackageVersion Include="SPB" Version="0.0.4-build32" />
     <PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
     <PackageVersion Include="System.Management" Version="8.0.0" />
     <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
   </ItemGroup>
-</Project>
+</Project>

+ 0 - 7
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);
 

+ 0 - 1
src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj

@@ -30,7 +30,6 @@
     <PackageReference Include="OpenTK.Graphics" />
     <PackageReference Include="SPB" />
     <PackageReference Include="SharpZipLib" />
-    <PackageReference Include="SixLabors.ImageSharp" />
   </ItemGroup>
 
   <ItemGroup>

+ 22 - 17
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<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 image = 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, image.GetPixels(), e.Data.Length);
+                        using var surface = SKSurface.Create(image.Info);
+                        var canvas = surface.Canvas;
 
-                        if (e.FlipY)
+                        if (e.FlipX || e.FlipY)
                         {
-                            image.Mutate(x => x.Flip(FlipMode.Vertical));
+                            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());
 
-                        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();
 

+ 18 - 11
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<Rgba32>(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();
         }

+ 4 - 6
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();
         }

+ 7 - 2
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;
 

+ 209 - 181
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<Argb32> _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);
-
-                context.Clear(Color.Transparent);
-                context.Fill(_panelBrush, _panelRectangle);
-                context.DrawImage(_ryujinxLogo, _logoPosition, 1);
+            canvas.Clear(SKColors.Transparent);
+            canvas.DrawRect(_panelRectangle, _panelBrush);
+            canvas.DrawBitmap(_ryujinxLogo, _logoPosition);
 
-                float halfWidth = _panelRectangle.Width / 2;
-                float buttonsY = _panelRectangle.Y + 185;
+            float halfWidth = _panelRectangle.Width / 2;
+            float buttonsY = _panelRectangle.Top + 185;
 
-                PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
+            SKPoint 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(canvas, state);
 
-                DrawTextBox(context, state);
+            float halfWidth = _panelRectangle.Width / 2;
+            float buttonsY = _panelRectangle.Top + 185;
 
-                float halfWidth = _panelRectangle.Width / 2;
-                float buttonsY = _panelRectangle.Y + 185;
+            SKPoint acceptButtonPosition = new(halfWidth - 180, buttonsY);
+            SKPoint cancelButtonPosition = new(halfWidth, buttonsY);
+            SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY);
 
-                PointF acceptButtonPosition = new(halfWidth - 180, buttonsY);
-                PointF cancelButtonPosition = new(halfWidth, buttonsY);
-                PointF 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<Argb32>((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<char> text, Font font)
+        private static SKRect MeasureString(ReadOnlySpan<char> 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<char> textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin);
                 ReadOnlySpan<char> 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<char> 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);
+
+                    canvas.DrawRect(cursorRectangle, cursorPen);
+                    canvas.DrawRect(cursorRectangle, cursorBrush);
 
-                    context.Draw(cursorPen, cursorRectangle);
-                    context.Fill(cursorBrush, cursorRectangle);
+                    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);
 
-                    Image<Argb32> textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height);
-                    textOverCursor.Mutate(context =>
+                    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
+            };
+
+            var labelRectangle = MeasureString(label, paint);
 
-            float labelPositionX = iconWidth + 8 - labelRectangle.X;
+            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<Argb32> 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<uint> dataConvert = MemoryMarshal.Cast<byte, uint>(_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);
             }
         }
 

+ 7 - 3
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<Rgba32>(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;
             }

+ 3 - 2
src/Ryujinx.HLE/Ryujinx.HLE.csproj

@@ -2,6 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>net8.0</TargetFramework>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
   <ItemGroup>
@@ -24,8 +25,8 @@
     <PackageReference Include="LibHac" />
     <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
     <PackageReference Include="MsgPack.Cli" />
-    <PackageReference Include="SixLabors.ImageSharp" />
-    <PackageReference Include="SixLabors.ImageSharp.Drawing" />
+    <PackageReference Include="SkiaSharp" />
+    <PackageReference Include="SkiaSharp.NativeAssets.Linux" />
     <PackageReference Include="NetCoreServer" />
   </ItemGroup>
 

+ 16 - 12
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<Rgba32>(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<Rgba32>(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
         /// <param name="source">The source bitmap image that will be saved as an .ico file</param>
         /// <param name="filePath">The location that the new .ico file will be saved too (Make sure to include '.ico' in the path).</param>
         [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));
         }
     }
 }

+ 2 - 7
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)

+ 3 - 0
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<KeyEventArgs> GetKeyDownRoutedEvent()
         {
             return KeyDownEvent;

+ 1 - 3
src/Ryujinx/UI/Windows/MainWindow.axaml

@@ -42,12 +42,10 @@
     </Window.KeyBindings>
     <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
         <Grid.RowDefinitions>
-            <RowDefinition Height="Auto" />
             <RowDefinition Height="*" />
         </Grid.RowDefinitions>
-        <helpers:OffscreenTextBox Name="HiddenTextBox" Grid.Row="0" />
+        <helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" />
         <Grid
-            Grid.Row="1"
             HorizontalAlignment="Stretch"
             VerticalAlignment="Stretch">
             <Grid.ColumnDefinitions>