diff options
author | Emmanuel Hansen <emmausssss@gmail.com> | 2024-08-31 14:32:53 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-31 11:32:53 -0300 |
commit | e0acde04bb032fd056904b909b3fd00c1a6fb996 (patch) | |
tree | c3f146228712153af6f277e0e874d83a56b31d06 | |
parent | 3c61d560c39d6edf897183fe33b8047c25d2d895 (diff) |
Replace ImageSharp with SkiaSharp everywhere (#7030)1.1.1381
* 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
-rw-r--r-- | Directory.Packages.props | 6 | ||||
-rw-r--r-- | src/Ryujinx.Gtk3/Program.cs | 7 | ||||
-rw-r--r-- | src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj | 1 | ||||
-rw-r--r-- | src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs | 39 | ||||
-rw-r--r-- | src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs | 29 | ||||
-rw-r--r-- | src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs | 10 | ||||
-rw-r--r-- | src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs | 9 | ||||
-rw-r--r-- | src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs | 390 | ||||
-rw-r--r-- | src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs | 10 | ||||
-rw-r--r-- | src/Ryujinx.HLE/Ryujinx.HLE.csproj | 5 | ||||
-rw-r--r-- | src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs | 28 | ||||
-rw-r--r-- | src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs | 9 | ||||
-rw-r--r-- | src/Ryujinx/UI/Helpers/OffscreenTextBox.cs | 3 | ||||
-rw-r--r-- | src/Ryujinx/UI/Windows/MainWindow.axaml | 4 |
14 files changed, 295 insertions, 255 deletions
diff --git a/Directory.Packages.props b/Directory.Packages.props index c31099072..8a9fdc3be 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props | |||
@@ -42,11 +42,11 @@ | |||
42 | <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> | 42 | <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> |
43 | <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" /> | 43 | <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" /> |
44 | <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" /> | 44 | <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" /> |
45 | <PackageVersion Include="SixLabors.ImageSharp" Version="2.1.9" /> | 45 | <PackageVersion Include="SkiaSharp" Version="2.88.7" /> |
46 | <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" /> | 46 | <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" /> |
47 | <PackageVersion Include="SPB" Version="0.0.4-build32" /> | 47 | <PackageVersion Include="SPB" Version="0.0.4-build32" /> |
48 | <PackageVersion Include="System.IO.Hashing" Version="8.0.0" /> | 48 | <PackageVersion Include="System.IO.Hashing" Version="8.0.0" /> |
49 | <PackageVersion Include="System.Management" Version="8.0.0" /> | 49 | <PackageVersion Include="System.Management" Version="8.0.0" /> |
50 | <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> | 50 | <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> |
51 | </ItemGroup> | 51 | </ItemGroup> |
52 | </Project> \ No newline at end of file | 52 | </Project> |
diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs index 8bb651640..745335ac9 100644 --- a/src/Ryujinx.Gtk3/Program.cs +++ b/src/Ryujinx.Gtk3/Program.cs | |||
@@ -13,7 +13,6 @@ using Ryujinx.UI.Common.Configuration; | |||
13 | using Ryujinx.UI.Common.Helper; | 13 | using Ryujinx.UI.Common.Helper; |
14 | using Ryujinx.UI.Common.SystemInfo; | 14 | using Ryujinx.UI.Common.SystemInfo; |
15 | using Ryujinx.UI.Widgets; | 15 | using Ryujinx.UI.Widgets; |
16 | using SixLabors.ImageSharp.Formats.Jpeg; | ||
17 | using System; | 16 | using System; |
18 | using System.Collections.Generic; | 17 | using System.Collections.Generic; |
19 | using System.Diagnostics; | 18 | using System.Diagnostics; |
@@ -162,12 +161,6 @@ namespace Ryujinx | |||
162 | }); | 161 | }); |
163 | }; | 162 | }; |
164 | 163 | ||
165 | // Sets ImageSharp Jpeg Encoder Quality. | ||
166 | SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder() | ||
167 | { | ||
168 | Quality = 100, | ||
169 | }); | ||
170 | |||
171 | string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); | 164 | string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); |
172 | string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); | 165 | string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); |
173 | 166 | ||
diff --git a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj index b4453f9d7..722d6080b 100644 --- a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj +++ b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj | |||
@@ -30,7 +30,6 @@ | |||
30 | <PackageReference Include="OpenTK.Graphics" /> | 30 | <PackageReference Include="OpenTK.Graphics" /> |
31 | <PackageReference Include="SPB" /> | 31 | <PackageReference Include="SPB" /> |
32 | <PackageReference Include="SharpZipLib" /> | 32 | <PackageReference Include="SharpZipLib" /> |
33 | <PackageReference Include="SixLabors.ImageSharp" /> | ||
34 | </ItemGroup> | 33 | </ItemGroup> |
35 | 34 | ||
36 | <ItemGroup> | 35 | <ItemGroup> |
diff --git a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs index 0e636792d..12139e87d 100644 --- a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs +++ b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs | |||
@@ -13,16 +13,13 @@ using Ryujinx.Input.HLE; | |||
13 | using Ryujinx.UI.Common.Configuration; | 13 | using Ryujinx.UI.Common.Configuration; |
14 | using Ryujinx.UI.Common.Helper; | 14 | using Ryujinx.UI.Common.Helper; |
15 | using Ryujinx.UI.Widgets; | 15 | using Ryujinx.UI.Widgets; |
16 | using SixLabors.ImageSharp; | 16 | using SkiaSharp; |
17 | using SixLabors.ImageSharp.Formats.Png; | ||
18 | using SixLabors.ImageSharp.PixelFormats; | ||
19 | using SixLabors.ImageSharp.Processing; | ||
20 | using System; | 17 | using System; |
21 | using System.Diagnostics; | 18 | using System.Diagnostics; |
22 | using System.IO; | 19 | using System.IO; |
20 | using System.Runtime.InteropServices; | ||
23 | using System.Threading; | 21 | using System.Threading; |
24 | using System.Threading.Tasks; | 22 | using System.Threading.Tasks; |
25 | using Image = SixLabors.ImageSharp.Image; | ||
26 | using Key = Ryujinx.Input.Key; | 23 | using Key = Ryujinx.Input.Key; |
27 | using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter; | 24 | using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter; |
28 | using Switch = Ryujinx.HLE.Switch; | 25 | using Switch = Ryujinx.HLE.Switch; |
@@ -404,23 +401,31 @@ namespace Ryujinx.UI | |||
404 | return; | 401 | return; |
405 | } | 402 | } |
406 | 403 | ||
407 | Image image = e.IsBgra ? Image.LoadPixelData<Bgra32>(e.Data, e.Width, e.Height) | 404 | var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888; |
408 | : Image.LoadPixelData<Rgba32>(e.Data, e.Width, e.Height); | 405 | using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); |
409 | 406 | ||
410 | if (e.FlipX) | 407 | Marshal.Copy(e.Data, 0, image.GetPixels(), e.Data.Length); |
411 | { | 408 | using var surface = SKSurface.Create(image.Info); |
412 | image.Mutate(x => x.Flip(FlipMode.Horizontal)); | 409 | var canvas = surface.Canvas; |
413 | } | ||
414 | 410 | ||
415 | if (e.FlipY) | 411 | if (e.FlipX || e.FlipY) |
416 | { | 412 | { |
417 | image.Mutate(x => x.Flip(FlipMode.Vertical)); | 413 | canvas.Clear(SKColors.Transparent); |
414 | |||
415 | float scaleX = e.FlipX ? -1 : 1; | ||
416 | float scaleY = e.FlipY ? -1 : 1; | ||
417 | |||
418 | var matrix = SKMatrix.CreateScale(scaleX, scaleY, image.Width / 2f, image.Height / 2f); | ||
419 | |||
420 | canvas.SetMatrix(matrix); | ||
418 | } | 421 | } |
422 | canvas.DrawBitmap(image, new SKPoint()); | ||
419 | 423 | ||
420 | image.SaveAsPng(path, new PngEncoder() | 424 | surface.Flush(); |
421 | { | 425 | using var snapshot = surface.Snapshot(); |
422 | ColorType = PngColorType.Rgb, | 426 | using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80); |
423 | }); | 427 | using var file = File.OpenWrite(path); |
428 | encoded.SaveTo(file); | ||
424 | 429 | ||
425 | image.Dispose(); | 430 | image.Dispose(); |
426 | 431 | ||
diff --git a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs index d9ecd47b7..fcd960df0 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; | |||
9 | using Ryujinx.Common.Memory; | 9 | using Ryujinx.Common.Memory; |
10 | using Ryujinx.HLE.FileSystem; | 10 | using Ryujinx.HLE.FileSystem; |
11 | using Ryujinx.UI.Common.Configuration; | 11 | using Ryujinx.UI.Common.Configuration; |
12 | using SixLabors.ImageSharp; | 12 | using SkiaSharp; |
13 | using SixLabors.ImageSharp.Formats.Png; | ||
14 | using SixLabors.ImageSharp.PixelFormats; | ||
15 | using SixLabors.ImageSharp.Processing; | ||
16 | using System; | 13 | using System; |
17 | using System.Buffers.Binary; | 14 | using System.Buffers.Binary; |
18 | using System.Collections.Generic; | 15 | using System.Collections.Generic; |
19 | using System.IO; | 16 | using System.IO; |
20 | using System.Reflection; | 17 | using System.Reflection; |
21 | using Image = SixLabors.ImageSharp.Image; | 18 | using System.Runtime.InteropServices; |
22 | 19 | ||
23 | namespace Ryujinx.UI.Windows | 20 | namespace Ryujinx.UI.Windows |
24 | { | 21 | { |
@@ -144,9 +141,11 @@ namespace Ryujinx.UI.Windows | |||
144 | 141 | ||
145 | stream.Position = 0; | 142 | stream.Position = 0; |
146 | 143 | ||
147 | Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); | 144 | using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888)); |
145 | var data = DecompressYaz0(stream); | ||
146 | Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length); | ||
148 | 147 | ||
149 | avatarImage.SaveAsPng(streamPng); | 148 | avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80); |
150 | 149 | ||
151 | _avatarDict.Add(item.FullPath, streamPng.ToArray()); | 150 | _avatarDict.Add(item.FullPath, streamPng.ToArray()); |
152 | } | 151 | } |
@@ -170,15 +169,23 @@ namespace Ryujinx.UI.Windows | |||
170 | { | 169 | { |
171 | using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); | 170 | using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); |
172 | 171 | ||
173 | Image avatarImage = Image.Load(data, new PngDecoder()); | 172 | using var avatarImage = SKBitmap.Decode(data); |
173 | using var surface = SKSurface.Create(avatarImage.Info); | ||
174 | 174 | ||
175 | avatarImage.Mutate(x => x.BackgroundColor(new Rgba32( | 175 | var background = new SKColor( |
176 | (byte)(_backgroundColor.Red * 255), | 176 | (byte)(_backgroundColor.Red * 255), |
177 | (byte)(_backgroundColor.Green * 255), | 177 | (byte)(_backgroundColor.Green * 255), |
178 | (byte)(_backgroundColor.Blue * 255), | 178 | (byte)(_backgroundColor.Blue * 255), |
179 | (byte)(_backgroundColor.Alpha * 255) | 179 | (byte)(_backgroundColor.Alpha * 255) |
180 | ))); | 180 | ); |
181 | avatarImage.SaveAsJpeg(streamJpg); | 181 | var canvas = surface.Canvas; |
182 | canvas.Clear(background); | ||
183 | canvas.DrawBitmap(avatarImage, new SKPoint()); | ||
184 | |||
185 | surface.Flush(); | ||
186 | using var snapshot = surface.Snapshot(); | ||
187 | using var encoded = snapshot.Encode(SKEncodedImageFormat.Jpeg, 80); | ||
188 | encoded.SaveTo(streamJpg); | ||
182 | 189 | ||
183 | return streamJpg.ToArray(); | 190 | return streamJpg.ToArray(); |
184 | } | 191 | } |
diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs index d1e5fa9fc..77afc5d1f 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; | |||
4 | using Ryujinx.HLE.HOS.Services.Account.Acc; | 4 | using Ryujinx.HLE.HOS.Services.Account.Acc; |
5 | using Ryujinx.UI.Common.Configuration; | 5 | using Ryujinx.UI.Common.Configuration; |
6 | using Ryujinx.UI.Widgets; | 6 | using Ryujinx.UI.Widgets; |
7 | using SixLabors.ImageSharp; | 7 | using SkiaSharp; |
8 | using SixLabors.ImageSharp.Processing; | ||
9 | using System; | 8 | using System; |
10 | using System.Collections.Generic; | 9 | using System.Collections.Generic; |
11 | using System.IO; | 10 | using System.IO; |
12 | using System.Reflection; | 11 | using System.Reflection; |
13 | using System.Threading; | 12 | using System.Threading; |
14 | using System.Threading.Tasks; | 13 | using System.Threading.Tasks; |
15 | using Image = SixLabors.ImageSharp.Image; | ||
16 | 14 | ||
17 | namespace Ryujinx.UI.Windows | 15 | namespace Ryujinx.UI.Windows |
18 | { | 16 | { |
@@ -177,13 +175,13 @@ namespace Ryujinx.UI.Windows | |||
177 | 175 | ||
178 | private void ProcessProfileImage(byte[] buffer) | 176 | private void ProcessProfileImage(byte[] buffer) |
179 | { | 177 | { |
180 | using Image image = Image.Load(buffer); | 178 | using var image = SKBitmap.Decode(buffer); |
181 | 179 | ||
182 | image.Mutate(x => x.Resize(256, 256)); | 180 | image.Resize(new SKImageInfo(256, 256), SKFilterQuality.High); |
183 | 181 | ||
184 | using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); | 182 | using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); |
185 | 183 | ||
186 | image.SaveAsJpeg(streamJpg); | 184 | image.Encode(streamJpg, SKEncodedImageFormat.Jpeg, 80); |
187 | 185 | ||
188 | _bufferImageProfile = streamJpg.ToArray(); | 186 | _bufferImageProfile = streamJpg.ToArray(); |
189 | } | 187 | } |
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs index 3f7516e6a..239535ad5 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 | |||
112 | { | 112 | { |
113 | // Update the parameters that were provided. | 113 | // Update the parameters that were provided. |
114 | _state.InputText = inputText ?? _state.InputText; | 114 | _state.InputText = inputText ?? _state.InputText; |
115 | _state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin); | 115 | _state.CursorBegin = Math.Max(0, cursorBegin.GetValueOrDefault(_state.CursorBegin)); |
116 | _state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd); | 116 | _state.CursorEnd = Math.Min(cursorEnd.GetValueOrDefault(_state.CursorEnd), _state.InputText.Length); |
117 | _state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode); | 117 | _state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode); |
118 | _state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled); | 118 | _state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled); |
119 | 119 | ||
120 | var begin = _state.CursorBegin; | ||
121 | var end = _state.CursorEnd; | ||
122 | _state.CursorBegin = Math.Min(begin, end); | ||
123 | _state.CursorEnd = Math.Max(begin, end); | ||
124 | |||
120 | // Reset the cursor blink. | 125 | // Reset the cursor blink. |
121 | _state.TextBoxBlinkCounter = 0; | 126 | _state.TextBoxBlinkCounter = 0; |
122 | 127 | ||
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index 9e48568e1..cc62eca1d 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs | |||
@@ -1,14 +1,9 @@ | |||
1 | using Ryujinx.HLE.UI; | 1 | using Ryujinx.HLE.UI; |
2 | using Ryujinx.Memory; | 2 | using Ryujinx.Memory; |
3 | using SixLabors.Fonts; | 3 | using SkiaSharp; |
4 | using SixLabors.ImageSharp; | ||
5 | using SixLabors.ImageSharp.Drawing.Processing; | ||
6 | using SixLabors.ImageSharp.PixelFormats; | ||
7 | using SixLabors.ImageSharp.Processing; | ||
8 | using System; | 4 | using System; |
9 | using System.Diagnostics; | 5 | using System.Diagnostics; |
10 | using System.IO; | 6 | using System.IO; |
11 | using System.Numerics; | ||
12 | using System.Reflection; | 7 | using System.Reflection; |
13 | using System.Runtime.InteropServices; | 8 | using System.Runtime.InteropServices; |
14 | 9 | ||
@@ -29,38 +24,39 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
29 | private readonly object _bufferLock = new(); | 24 | private readonly object _bufferLock = new(); |
30 | 25 | ||
31 | private RenderingSurfaceInfo _surfaceInfo = null; | 26 | private RenderingSurfaceInfo _surfaceInfo = null; |
32 | private Image<Argb32> _surface = null; | 27 | private SKImageInfo _imageInfo; |
28 | private SKSurface _surface = null; | ||
33 | private byte[] _bufferData = null; | 29 | private byte[] _bufferData = null; |
34 | 30 | ||
35 | private readonly Image _ryujinxLogo = null; | 31 | private readonly SKBitmap _ryujinxLogo = null; |
36 | private readonly Image _padAcceptIcon = null; | 32 | private readonly SKBitmap _padAcceptIcon = null; |
37 | private readonly Image _padCancelIcon = null; | 33 | private readonly SKBitmap _padCancelIcon = null; |
38 | private readonly Image _keyModeIcon = null; | 34 | private readonly SKBitmap _keyModeIcon = null; |
39 | 35 | ||
40 | private readonly float _textBoxOutlineWidth; | 36 | private readonly float _textBoxOutlineWidth; |
41 | private readonly float _padPressedPenWidth; | 37 | private readonly float _padPressedPenWidth; |
42 | 38 | ||
43 | private readonly Color _textNormalColor; | 39 | private readonly SKColor _textNormalColor; |
44 | private readonly Color _textSelectedColor; | 40 | private readonly SKColor _textSelectedColor; |
45 | private readonly Color _textOverCursorColor; | 41 | private readonly SKColor _textOverCursorColor; |
46 | 42 | ||
47 | private readonly Brush _panelBrush; | 43 | private readonly SKPaint _panelBrush; |
48 | private readonly Brush _disabledBrush; | 44 | private readonly SKPaint _disabledBrush; |
49 | private readonly Brush _cursorBrush; | 45 | private readonly SKPaint _cursorBrush; |
50 | private readonly Brush _selectionBoxBrush; | 46 | private readonly SKPaint _selectionBoxBrush; |
51 | 47 | ||
52 | private readonly Pen _textBoxOutlinePen; | 48 | private readonly SKPaint _textBoxOutlinePen; |
53 | private readonly Pen _cursorPen; | 49 | private readonly SKPaint _cursorPen; |
54 | private readonly Pen _selectionBoxPen; | 50 | private readonly SKPaint _selectionBoxPen; |
55 | private readonly Pen _padPressedPen; | 51 | private readonly SKPaint _padPressedPen; |
56 | 52 | ||
57 | private readonly int _inputTextFontSize; | 53 | private readonly int _inputTextFontSize; |
58 | private Font _messageFont; | 54 | private SKFont _messageFont; |
59 | private Font _inputTextFont; | 55 | private SKFont _inputTextFont; |
60 | private Font _labelsTextFont; | 56 | private SKFont _labelsTextFont; |
61 | 57 | ||
62 | private RectangleF _panelRectangle; | 58 | private SKRect _panelRectangle; |
63 | private Point _logoPosition; | 59 | private SKPoint _logoPosition; |
64 | private float _messagePositionY; | 60 | private float _messagePositionY; |
65 | 61 | ||
66 | public SoftwareKeyboardRendererBase(IHostUITheme uiTheme) | 62 | public SoftwareKeyboardRendererBase(IHostUITheme uiTheme) |
@@ -78,10 +74,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
78 | _padCancelIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, padCancelIconPath, 0, 0); | 74 | _padCancelIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, padCancelIconPath, 0, 0); |
79 | _keyModeIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, keyModeIconPath, 0, 0); | 75 | _keyModeIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, keyModeIconPath, 0, 0); |
80 | 76 | ||
81 | Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255); | 77 | var panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255); |
82 | Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150); | 78 | var panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150); |
83 | Color borderColor = ToColor(uiTheme.DefaultBorderColor); | 79 | var borderColor = ToColor(uiTheme.DefaultBorderColor); |
84 | Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor); | 80 | var selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor); |
85 | 81 | ||
86 | _textNormalColor = ToColor(uiTheme.DefaultForegroundColor); | 82 | _textNormalColor = ToColor(uiTheme.DefaultForegroundColor); |
87 | _textSelectedColor = ToColor(uiTheme.SelectionForegroundColor); | 83 | _textSelectedColor = ToColor(uiTheme.SelectionForegroundColor); |
@@ -92,15 +88,29 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
92 | _textBoxOutlineWidth = 2; | 88 | _textBoxOutlineWidth = 2; |
93 | _padPressedPenWidth = 2; | 89 | _padPressedPenWidth = 2; |
94 | 90 | ||
95 | _panelBrush = new SolidBrush(panelColor); | 91 | _panelBrush = new SKPaint() |
96 | _disabledBrush = new SolidBrush(panelTransparentColor); | 92 | { |
97 | _cursorBrush = new SolidBrush(_textNormalColor); | 93 | Color = panelColor, |
98 | _selectionBoxBrush = new SolidBrush(selectionBackgroundColor); | 94 | IsAntialias = true |
95 | }; | ||
96 | _disabledBrush = new SKPaint() | ||
97 | { | ||
98 | Color = panelTransparentColor, | ||
99 | IsAntialias = true | ||
100 | }; | ||
101 | _cursorBrush = new SKPaint() { Color = _textNormalColor, IsAntialias = true }; | ||
102 | _selectionBoxBrush = new SKPaint() { Color = selectionBackgroundColor, IsAntialias = true }; | ||
99 | 103 | ||
100 | _textBoxOutlinePen = Pens.Solid(borderColor, _textBoxOutlineWidth); | 104 | _textBoxOutlinePen = new SKPaint() |
101 | _cursorPen = Pens.Solid(_textNormalColor, cursorWidth); | 105 | { |
102 | _selectionBoxPen = Pens.Solid(selectionBackgroundColor, cursorWidth); | 106 | Color = borderColor, |
103 | _padPressedPen = Pens.Solid(borderColor, _padPressedPenWidth); | 107 | StrokeWidth = _textBoxOutlineWidth, |
108 | IsStroke = true, | ||
109 | IsAntialias = true | ||
110 | }; | ||
111 | _cursorPen = new SKPaint() { Color = _textNormalColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true }; | ||
112 | _selectionBoxPen = new SKPaint() { Color = selectionBackgroundColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true }; | ||
113 | _padPressedPen = new SKPaint() { Color = borderColor, StrokeWidth = _padPressedPenWidth, IsStroke = true, IsAntialias = true }; | ||
104 | 114 | ||
105 | _inputTextFontSize = 20; | 115 | _inputTextFontSize = 20; |
106 | 116 | ||
@@ -123,9 +133,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
123 | { | 133 | { |
124 | try | 134 | try |
125 | { | 135 | { |
126 | _messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular); | 136 | using var typeface = SKTypeface.FromFamilyName(fontFamily, SKFontStyle.Normal); |
127 | _inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular); | 137 | _messageFont = new SKFont(typeface, 26); |
128 | _labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular); | 138 | _inputTextFont = new SKFont(typeface, _inputTextFontSize); |
139 | _labelsTextFont = new SKFont(typeface, 24); | ||
129 | 140 | ||
130 | return; | 141 | return; |
131 | } | 142 | } |
@@ -137,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
137 | throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!"); | 148 | throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!"); |
138 | } | 149 | } |
139 | 150 | ||
140 | private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false) | 151 | private static SKColor ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false) |
141 | { | 152 | { |
142 | var a = (byte)(color.A * 255); | 153 | var a = (byte)(color.A * 255); |
143 | var r = (byte)(color.R * 255); | 154 | var r = (byte)(color.R * 255); |
@@ -151,34 +162,33 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
151 | b = (byte)(255 - b); | 162 | b = (byte)(255 - b); |
152 | } | 163 | } |
153 | 164 | ||
154 | return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a)); | 165 | return new SKColor(r, g, b, overrideAlpha.GetValueOrDefault(a)); |
155 | } | 166 | } |
156 | 167 | ||
157 | private static Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight) | 168 | private static SKBitmap LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight) |
158 | { | 169 | { |
159 | Stream resourceStream = assembly.GetManifestResourceStream(resourcePath); | 170 | Stream resourceStream = assembly.GetManifestResourceStream(resourcePath); |
160 | 171 | ||
161 | return LoadResource(resourceStream, newWidth, newHeight); | 172 | return LoadResource(resourceStream, newWidth, newHeight); |
162 | } | 173 | } |
163 | 174 | ||
164 | private static Image LoadResource(Stream resourceStream, int newWidth, int newHeight) | 175 | private static SKBitmap LoadResource(Stream resourceStream, int newWidth, int newHeight) |
165 | { | 176 | { |
166 | Debug.Assert(resourceStream != null); | 177 | Debug.Assert(resourceStream != null); |
167 | 178 | ||
168 | var image = Image.Load(resourceStream); | 179 | var bitmap = SKBitmap.Decode(resourceStream); |
169 | 180 | ||
170 | if (newHeight != 0 && newWidth != 0) | 181 | if (newHeight != 0 && newWidth != 0) |
171 | { | 182 | { |
172 | image.Mutate(x => x.Resize(newWidth, newHeight, KnownResamplers.Lanczos3)); | 183 | var resized = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKFilterQuality.High); |
184 | if (resized != null) | ||
185 | { | ||
186 | bitmap.Dispose(); | ||
187 | bitmap = resized; | ||
188 | } | ||
173 | } | 189 | } |
174 | 190 | ||
175 | return image; | 191 | return bitmap; |
176 | } | ||
177 | |||
178 | private static void SetGraphicsOptions(IImageProcessingContext context) | ||
179 | { | ||
180 | context.GetGraphicsOptions().Antialias = true; | ||
181 | context.GetDrawingOptions().GraphicsOptions.Antialias = true; | ||
182 | } | 192 | } |
183 | 193 | ||
184 | private void DrawImmutableElements() | 194 | private void DrawImmutableElements() |
@@ -187,22 +197,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
187 | { | 197 | { |
188 | return; | 198 | return; |
189 | } | 199 | } |
200 | var canvas = _surface.Canvas; | ||
190 | 201 | ||
191 | _surface.Mutate(context => | 202 | canvas.Clear(SKColors.Transparent); |
192 | { | 203 | canvas.DrawRect(_panelRectangle, _panelBrush); |
193 | SetGraphicsOptions(context); | 204 | canvas.DrawBitmap(_ryujinxLogo, _logoPosition); |
194 | |||
195 | context.Clear(Color.Transparent); | ||
196 | context.Fill(_panelBrush, _panelRectangle); | ||
197 | context.DrawImage(_ryujinxLogo, _logoPosition, 1); | ||
198 | 205 | ||
199 | float halfWidth = _panelRectangle.Width / 2; | 206 | float halfWidth = _panelRectangle.Width / 2; |
200 | float buttonsY = _panelRectangle.Y + 185; | 207 | float buttonsY = _panelRectangle.Top + 185; |
201 | 208 | ||
202 | PointF disableButtonPosition = new(halfWidth + 180, buttonsY); | 209 | SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY); |
203 | 210 | ||
204 | DrawControllerToggle(context, disableButtonPosition); | 211 | DrawControllerToggle(canvas, disableButtonPosition); |
205 | }); | ||
206 | } | 212 | } |
207 | 213 | ||
208 | public void DrawMutableElements(SoftwareKeyboardUIState state) | 214 | public void DrawMutableElements(SoftwareKeyboardUIState state) |
@@ -212,40 +218,43 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
212 | return; | 218 | return; |
213 | } | 219 | } |
214 | 220 | ||
215 | _surface.Mutate(context => | 221 | using var paint = new SKPaint(_messageFont) |
216 | { | 222 | { |
217 | var messageRectangle = MeasureString(MessageText, _messageFont); | 223 | Color = _textNormalColor, |
218 | float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X; | 224 | IsAntialias = true |
219 | float messagePositionY = _messagePositionY - messageRectangle.Y; | 225 | }; |
220 | var messagePosition = new PointF(messagePositionX, messagePositionY); | ||
221 | var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height); | ||
222 | 226 | ||
223 | SetGraphicsOptions(context); | 227 | var canvas = _surface.Canvas; |
228 | var messageRectangle = MeasureString(MessageText, paint); | ||
229 | float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.Left; | ||
230 | float messagePositionY = _messagePositionY - messageRectangle.Top; | ||
231 | var messagePosition = new SKPoint(messagePositionX, messagePositionY); | ||
232 | var messageBoundRectangle = SKRect.Create(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height); | ||
224 | 233 | ||
225 | context.Fill(_panelBrush, messageBoundRectangle); | 234 | canvas.DrawRect(messageBoundRectangle, _panelBrush); |
226 | 235 | ||
227 | context.DrawText(MessageText, _messageFont, _textNormalColor, messagePosition); | 236 | canvas.DrawText(MessageText, messagePosition.X, messagePosition.Y + _messageFont.Metrics.XHeight + _messageFont.Metrics.Descent, paint); |
228 | 237 | ||
229 | if (!state.TypingEnabled) | 238 | if (!state.TypingEnabled) |
230 | { | 239 | { |
231 | // Just draw a semi-transparent rectangle on top to fade the component with the background. | 240 | // Just draw a semi-transparent rectangle on top to fade the component with the background. |
232 | // TODO (caian): This will not work if one decides to add make background semi-transparent as well. | 241 | // TODO (caian): This will not work if one decides to add make background semi-transparent as well. |
233 | 242 | ||
234 | context.Fill(_disabledBrush, messageBoundRectangle); | 243 | canvas.DrawRect(messageBoundRectangle, _disabledBrush); |
235 | } | 244 | } |
245 | |||
246 | DrawTextBox(canvas, state); | ||
236 | 247 | ||
237 | DrawTextBox(context, state); | 248 | float halfWidth = _panelRectangle.Width / 2; |
249 | float buttonsY = _panelRectangle.Top + 185; | ||
238 | 250 | ||
239 | float halfWidth = _panelRectangle.Width / 2; | 251 | SKPoint acceptButtonPosition = new(halfWidth - 180, buttonsY); |
240 | float buttonsY = _panelRectangle.Y + 185; | 252 | SKPoint cancelButtonPosition = new(halfWidth, buttonsY); |
253 | SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY); | ||
241 | 254 | ||
242 | PointF acceptButtonPosition = new(halfWidth - 180, buttonsY); | 255 | DrawPadButton(canvas, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled); |
243 | PointF cancelButtonPosition = new(halfWidth, buttonsY); | 256 | DrawPadButton(canvas, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled); |
244 | PointF disableButtonPosition = new(halfWidth + 180, buttonsY); | ||
245 | 257 | ||
246 | DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled); | ||
247 | DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled); | ||
248 | }); | ||
249 | } | 258 | } |
250 | 259 | ||
251 | public void CreateSurface(RenderingSurfaceInfo surfaceInfo) | 260 | public void CreateSurface(RenderingSurfaceInfo surfaceInfo) |
@@ -268,7 +277,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
268 | Debug.Assert(_surfaceInfo.Height <= totalHeight); | 277 | Debug.Assert(_surfaceInfo.Height <= totalHeight); |
269 | Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size); | 278 | Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size); |
270 | 279 | ||
271 | _surface = new Image<Argb32>((int)totalWidth, (int)totalHeight); | 280 | _imageInfo = new SKImageInfo((int)totalWidth, (int)totalHeight, SKColorType.Rgba8888); |
281 | _surface = SKSurface.Create(_imageInfo); | ||
272 | 282 | ||
273 | ComputeConstants(); | 283 | ComputeConstants(); |
274 | DrawImmutableElements(); | 284 | DrawImmutableElements(); |
@@ -282,76 +292,81 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
282 | int panelHeight = 240; | 292 | int panelHeight = 240; |
283 | int panelPositionY = totalHeight - panelHeight; | 293 | int panelPositionY = totalHeight - panelHeight; |
284 | 294 | ||
285 | _panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight); | 295 | _panelRectangle = SKRect.Create(0, panelPositionY, totalWidth, panelHeight); |
286 | 296 | ||
287 | _messagePositionY = panelPositionY + 60; | 297 | _messagePositionY = panelPositionY + 60; |
288 | 298 | ||
289 | int logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2; | 299 | int logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2; |
290 | int logoPositionY = panelPositionY + 18; | 300 | int logoPositionY = panelPositionY + 18; |
291 | 301 | ||
292 | _logoPosition = new Point(logoPositionX, logoPositionY); | 302 | _logoPosition = new SKPoint(logoPositionX, logoPositionY); |
293 | } | 303 | } |
294 | private static RectangleF MeasureString(string text, Font font) | 304 | private static SKRect MeasureString(string text, SKPaint paint) |
295 | { | 305 | { |
296 | TextOptions options = new(font); | 306 | SKRect bounds = SKRect.Empty; |
297 | 307 | ||
298 | if (text == "") | 308 | if (text == "") |
299 | { | 309 | { |
300 | FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options); | 310 | paint.MeasureText(" ", ref bounds); |
301 | 311 | } | |
302 | return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height); | 312 | else |
313 | { | ||
314 | paint.MeasureText(text, ref bounds); | ||
303 | } | 315 | } |
304 | 316 | ||
305 | FontRectangle rectangle = TextMeasurer.MeasureSize(text, options); | 317 | return bounds; |
306 | |||
307 | return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); | ||
308 | } | 318 | } |
309 | 319 | ||
310 | private static RectangleF MeasureString(ReadOnlySpan<char> text, Font font) | 320 | private static SKRect MeasureString(ReadOnlySpan<char> text, SKPaint paint) |
311 | { | 321 | { |
312 | TextOptions options = new(font); | 322 | SKRect bounds = SKRect.Empty; |
313 | 323 | ||
314 | if (text == "") | 324 | if (text == "") |
315 | { | 325 | { |
316 | FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options); | 326 | paint.MeasureText(" ", ref bounds); |
317 | return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height); | 327 | } |
328 | else | ||
329 | { | ||
330 | paint.MeasureText(text, ref bounds); | ||
318 | } | 331 | } |
319 | 332 | ||
320 | FontRectangle rectangle = TextMeasurer.MeasureSize(text, options); | 333 | return bounds; |
321 | |||
322 | return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); | ||
323 | } | 334 | } |
324 | 335 | ||
325 | private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUIState state) | 336 | private void DrawTextBox(SKCanvas canvas, SoftwareKeyboardUIState state) |
326 | { | 337 | { |
327 | var inputTextRectangle = MeasureString(state.InputText, _inputTextFont); | 338 | using var textPaint = new SKPaint(_labelsTextFont) |
339 | { | ||
340 | IsAntialias = true, | ||
341 | Color = _textNormalColor | ||
342 | }; | ||
343 | var inputTextRectangle = MeasureString(state.InputText, textPaint); | ||
328 | 344 | ||
329 | float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8)); | 345 | float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.Left + 8)); |
330 | float boxHeight = 32; | 346 | float boxHeight = 32; |
331 | float boxY = _panelRectangle.Y + 110; | 347 | float boxY = _panelRectangle.Top + 110; |
332 | float boxX = (int)((_panelRectangle.Width - boxWidth) / 2); | 348 | float boxX = (int)((_panelRectangle.Width - boxWidth) / 2); |
333 | 349 | ||
334 | RectangleF boxRectangle = new(boxX, boxY, boxWidth, boxHeight); | 350 | SKRect boxRectangle = SKRect.Create(boxX, boxY, boxWidth, boxHeight); |
335 | 351 | ||
336 | RectangleF boundRectangle = new(_panelRectangle.X, boxY - _textBoxOutlineWidth, | 352 | SKRect boundRectangle = SKRect.Create(_panelRectangle.Left, boxY - _textBoxOutlineWidth, |
337 | _panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth); | 353 | _panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth); |
338 | 354 | ||
339 | context.Fill(_panelBrush, boundRectangle); | 355 | canvas.DrawRect(boundRectangle, _panelBrush); |
340 | 356 | ||
341 | context.Draw(_textBoxOutlinePen, boxRectangle); | 357 | canvas.DrawRect(boxRectangle, _textBoxOutlinePen); |
342 | 358 | ||
343 | float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X; | 359 | float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.Left; |
344 | float inputTextY = boxY + 5; | 360 | float inputTextY = boxY + 5; |
345 | 361 | ||
346 | var inputTextPosition = new PointF(inputTextX, inputTextY); | 362 | var inputTextPosition = new SKPoint(inputTextX, inputTextY); |
347 | 363 | canvas.DrawText(state.InputText, inputTextPosition.X, inputTextPosition.Y + (_labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent), textPaint); | |
348 | context.DrawText(state.InputText, _inputTextFont, _textNormalColor, inputTextPosition); | ||
349 | 364 | ||
350 | // Draw the cursor on top of the text and redraw the text with a different color if necessary. | 365 | // Draw the cursor on top of the text and redraw the text with a different color if necessary. |
351 | 366 | ||
352 | Color cursorTextColor; | 367 | SKColor cursorTextColor; |
353 | Brush cursorBrush; | 368 | SKPaint cursorBrush; |
354 | Pen cursorPen; | 369 | SKPaint cursorPen; |
355 | 370 | ||
356 | float cursorPositionYTop = inputTextY + 1; | 371 | float cursorPositionYTop = inputTextY + 1; |
357 | float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1; | 372 | float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1; |
@@ -371,12 +386,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
371 | ReadOnlySpan<char> textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin); | 386 | ReadOnlySpan<char> textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin); |
372 | ReadOnlySpan<char> textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd); | 387 | ReadOnlySpan<char> textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd); |
373 | 388 | ||
374 | var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont); | 389 | var selectionBeginRectangle = MeasureString(textUntilBegin, textPaint); |
375 | var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont); | 390 | var selectionEndRectangle = MeasureString(textUntilEnd, textPaint); |
376 | 391 | ||
377 | cursorVisible = true; | 392 | cursorVisible = true; |
378 | cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X; | 393 | cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.Left; |
379 | cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X; | 394 | cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.Left; |
380 | } | 395 | } |
381 | else | 396 | else |
382 | { | 397 | { |
@@ -390,10 +405,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
390 | 405 | ||
391 | int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); | 406 | int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin); |
392 | ReadOnlySpan<char> textUntilCursor = state.InputText.AsSpan(0, cursorBegin); | 407 | ReadOnlySpan<char> textUntilCursor = state.InputText.AsSpan(0, cursorBegin); |
393 | var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); | 408 | var cursorTextRectangle = MeasureString(textUntilCursor, textPaint); |
394 | 409 | ||
395 | cursorVisible = true; | 410 | cursorVisible = true; |
396 | cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; | 411 | cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left; |
397 | 412 | ||
398 | if (state.OverwriteMode) | 413 | if (state.OverwriteMode) |
399 | { | 414 | { |
@@ -402,8 +417,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
402 | if (state.CursorBegin < state.InputText.Length) | 417 | if (state.CursorBegin < state.InputText.Length) |
403 | { | 418 | { |
404 | textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1); | 419 | textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1); |
405 | cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont); | 420 | cursorTextRectangle = MeasureString(textUntilCursor, textPaint); |
406 | cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X; | 421 | cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left; |
407 | } | 422 | } |
408 | else | 423 | else |
409 | { | 424 | { |
@@ -430,29 +445,32 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
430 | 445 | ||
431 | if (cursorWidth == 0) | 446 | if (cursorWidth == 0) |
432 | { | 447 | { |
433 | PointF[] points = { | 448 | canvas.DrawLine(new SKPoint(cursorPositionXLeft, cursorPositionYTop), |
434 | new PointF(cursorPositionXLeft, cursorPositionYTop), | 449 | new SKPoint(cursorPositionXLeft, cursorPositionYBottom), |
435 | new PointF(cursorPositionXLeft, cursorPositionYBottom), | 450 | cursorPen); |
436 | }; | ||
437 | |||
438 | context.DrawLine(cursorPen, points); | ||
439 | } | 451 | } |
440 | else | 452 | else |
441 | { | 453 | { |
442 | var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight); | 454 | var cursorRectangle = SKRect.Create(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight); |
455 | |||
456 | canvas.DrawRect(cursorRectangle, cursorPen); | ||
457 | canvas.DrawRect(cursorRectangle, cursorBrush); | ||
443 | 458 | ||
444 | context.Draw(cursorPen, cursorRectangle); | 459 | using var textOverCursor = SKSurface.Create(new SKImageInfo((int)cursorRectangle.Width, (int)cursorRectangle.Height, SKColorType.Rgba8888)); |
445 | context.Fill(cursorBrush, cursorRectangle); | 460 | var textOverCanvas = textOverCursor.Canvas; |
461 | var textRelativePosition = new SKPoint(inputTextPosition.X - cursorRectangle.Left, inputTextPosition.Y - cursorRectangle.Top); | ||
446 | 462 | ||
447 | Image<Argb32> textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height); | 463 | using var cursorPaint = new SKPaint(_inputTextFont) |
448 | textOverCursor.Mutate(context => | ||
449 | { | 464 | { |
450 | var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y); | 465 | Color = cursorTextColor, |
451 | context.DrawText(state.InputText, _inputTextFont, cursorTextColor, textRelativePosition); | 466 | IsAntialias = true |
452 | }); | 467 | }; |
453 | 468 | ||
454 | var cursorPosition = new Point((int)cursorRectangle.X, (int)cursorRectangle.Y); | 469 | textOverCanvas.DrawText(state.InputText, textRelativePosition.X, textRelativePosition.Y + _inputTextFont.Metrics.XHeight + _inputTextFont.Metrics.Descent, cursorPaint); |
455 | context.DrawImage(textOverCursor, cursorPosition, 1); | 470 | |
471 | var cursorPosition = new SKPoint((int)cursorRectangle.Left, (int)cursorRectangle.Top); | ||
472 | textOverCursor.Flush(); | ||
473 | canvas.DrawSurface(textOverCursor, cursorPosition); | ||
456 | } | 474 | } |
457 | } | 475 | } |
458 | else if (!state.TypingEnabled) | 476 | else if (!state.TypingEnabled) |
@@ -460,11 +478,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
460 | // Just draw a semi-transparent rectangle on top to fade the component with the background. | 478 | // Just draw a semi-transparent rectangle on top to fade the component with the background. |
461 | // TODO (caian): This will not work if one decides to add make background semi-transparent as well. | 479 | // TODO (caian): This will not work if one decides to add make background semi-transparent as well. |
462 | 480 | ||
463 | context.Fill(_disabledBrush, boundRectangle); | 481 | canvas.DrawRect(boundRectangle, _disabledBrush); |
464 | } | 482 | } |
465 | } | 483 | } |
466 | 484 | ||
467 | private void DrawPadButton(IImageProcessingContext context, PointF point, Image icon, string label, bool pressed, bool enabled) | 485 | private void DrawPadButton(SKCanvas canvas, SKPoint point, SKBitmap icon, string label, bool pressed, bool enabled) |
468 | { | 486 | { |
469 | // Use relative positions so we can center the entire drawing later. | 487 | // Use relative positions so we can center the entire drawing later. |
470 | 488 | ||
@@ -473,12 +491,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
473 | float iconWidth = icon.Width; | 491 | float iconWidth = icon.Width; |
474 | float iconHeight = icon.Height; | 492 | float iconHeight = icon.Height; |
475 | 493 | ||
476 | var labelRectangle = MeasureString(label, _labelsTextFont); | 494 | using var paint = new SKPaint(_labelsTextFont) |
495 | { | ||
496 | Color = _textNormalColor, | ||
497 | IsAntialias = true | ||
498 | }; | ||
499 | |||
500 | var labelRectangle = MeasureString(label, paint); | ||
477 | 501 | ||
478 | float labelPositionX = iconWidth + 8 - labelRectangle.X; | 502 | float labelPositionX = iconWidth + 8 - labelRectangle.Left; |
479 | float labelPositionY = 3; | 503 | float labelPositionY = 3; |
480 | 504 | ||
481 | float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X; | 505 | float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.Left; |
482 | float fullHeight = iconHeight; | 506 | float fullHeight = iconHeight; |
483 | 507 | ||
484 | // Convert all relative positions into absolute. | 508 | // Convert all relative positions into absolute. |
@@ -489,24 +513,24 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
489 | iconX += originX; | 513 | iconX += originX; |
490 | iconY += originY; | 514 | iconY += originY; |
491 | 515 | ||
492 | var iconPosition = new Point((int)iconX, (int)iconY); | 516 | var iconPosition = new SKPoint((int)iconX, (int)iconY); |
493 | var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY); | 517 | var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY); |
494 | 518 | ||
495 | var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth, | 519 | var selectedRectangle = SKRect.Create(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth, |
496 | fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth); | 520 | fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth); |
497 | 521 | ||
498 | var boundRectangle = new RectangleF(originX, originY, fullWidth, fullHeight); | 522 | var boundRectangle = SKRect.Create(originX, originY, fullWidth, fullHeight); |
499 | boundRectangle.Inflate(4 * _padPressedPenWidth, 4 * _padPressedPenWidth); | 523 | boundRectangle.Inflate(4 * _padPressedPenWidth, 4 * _padPressedPenWidth); |
500 | 524 | ||
501 | context.Fill(_panelBrush, boundRectangle); | 525 | canvas.DrawRect(boundRectangle, _panelBrush); |
502 | context.DrawImage(icon, iconPosition, 1); | 526 | canvas.DrawBitmap(icon, iconPosition); |
503 | context.DrawText(label, _labelsTextFont, _textNormalColor, labelPosition); | 527 | canvas.DrawText(label, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent, paint); |
504 | 528 | ||
505 | if (enabled) | 529 | if (enabled) |
506 | { | 530 | { |
507 | if (pressed) | 531 | if (pressed) |
508 | { | 532 | { |
509 | context.Draw(_padPressedPen, selectedRectangle); | 533 | canvas.DrawRect(selectedRectangle, _padPressedPen); |
510 | } | 534 | } |
511 | } | 535 | } |
512 | else | 536 | else |
@@ -514,21 +538,26 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
514 | // Just draw a semi-transparent rectangle on top to fade the component with the background. | 538 | // Just draw a semi-transparent rectangle on top to fade the component with the background. |
515 | // TODO (caian): This will not work if one decides to add make background semi-transparent as well. | 539 | // TODO (caian): This will not work if one decides to add make background semi-transparent as well. |
516 | 540 | ||
517 | context.Fill(_disabledBrush, boundRectangle); | 541 | canvas.DrawRect(boundRectangle, _disabledBrush); |
518 | } | 542 | } |
519 | } | 543 | } |
520 | 544 | ||
521 | private void DrawControllerToggle(IImageProcessingContext context, PointF point) | 545 | private void DrawControllerToggle(SKCanvas canvas, SKPoint point) |
522 | { | 546 | { |
523 | var labelRectangle = MeasureString(ControllerToggleText, _labelsTextFont); | 547 | using var paint = new SKPaint(_labelsTextFont) |
548 | { | ||
549 | IsAntialias = true, | ||
550 | Color = _textNormalColor | ||
551 | }; | ||
552 | var labelRectangle = MeasureString(ControllerToggleText, paint); | ||
524 | 553 | ||
525 | // Use relative positions so we can center the entire drawing later. | 554 | // Use relative positions so we can center the entire drawing later. |
526 | 555 | ||
527 | float keyWidth = _keyModeIcon.Width; | 556 | float keyWidth = _keyModeIcon.Width; |
528 | float keyHeight = _keyModeIcon.Height; | 557 | float keyHeight = _keyModeIcon.Height; |
529 | 558 | ||
530 | float labelPositionX = keyWidth + 8 - labelRectangle.X; | 559 | float labelPositionX = keyWidth + 8 - labelRectangle.Left; |
531 | float labelPositionY = -labelRectangle.Y - 1; | 560 | float labelPositionY = -labelRectangle.Top - 1; |
532 | 561 | ||
533 | float keyX = 0; | 562 | float keyX = 0; |
534 | float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2); | 563 | float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2); |
@@ -544,14 +573,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
544 | keyX += originX; | 573 | keyX += originX; |
545 | keyY += originY; | 574 | keyY += originY; |
546 | 575 | ||
547 | var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY); | 576 | var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY); |
548 | var overlayPosition = new Point((int)keyX, (int)keyY); | 577 | var overlayPosition = new SKPoint((int)keyX, (int)keyY); |
549 | 578 | ||
550 | context.DrawImage(_keyModeIcon, overlayPosition, 1); | 579 | canvas.DrawBitmap(_keyModeIcon, overlayPosition); |
551 | context.DrawText(ControllerToggleText, _labelsTextFont, _textNormalColor, labelPosition); | 580 | canvas.DrawText(ControllerToggleText, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight, paint); |
552 | } | 581 | } |
553 | 582 | ||
554 | public void CopyImageToBuffer() | 583 | public unsafe void CopyImageToBuffer() |
555 | { | 584 | { |
556 | lock (_bufferLock) | 585 | lock (_bufferLock) |
557 | { | 586 | { |
@@ -561,21 +590,20 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard | |||
561 | } | 590 | } |
562 | 591 | ||
563 | // Convert the pixel format used in the image to the one used in the Switch surface. | 592 | // Convert the pixel format used in the image to the one used in the Switch surface. |
593 | _surface.Flush(); | ||
564 | 594 | ||
565 | if (!_surface.DangerousTryGetSinglePixelMemory(out Memory<Argb32> pixels)) | 595 | var buffer = new byte[_imageInfo.BytesSize]; |
596 | fixed (byte* bufferPtr = buffer) | ||
566 | { | 597 | { |
567 | return; | 598 | if (!_surface.ReadPixels(_imageInfo, (nint)bufferPtr, _imageInfo.RowBytes, 0, 0)) |
599 | { | ||
600 | return; | ||
601 | } | ||
568 | } | 602 | } |
569 | 603 | ||
570 | _bufferData = MemoryMarshal.AsBytes(pixels.Span).ToArray(); | 604 | _bufferData = buffer; |
571 | Span<uint> dataConvert = MemoryMarshal.Cast<byte, uint>(_bufferData); | ||
572 | 605 | ||
573 | Debug.Assert(_bufferData.Length == _surfaceInfo.Size); | 606 | Debug.Assert(buffer.Length == _surfaceInfo.Size); |
574 | |||
575 | for (int i = 0; i < dataConvert.Length; i++) | ||
576 | { | ||
577 | dataConvert[i] = BitOperations.RotateRight(dataConvert[i], 8); | ||
578 | } | ||
579 | } | 607 | } |
580 | } | 608 | } |
581 | 609 | ||
diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs index 91a8958e6..bf0c7e9dc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs | |||
@@ -1,10 +1,10 @@ | |||
1 | using Ryujinx.Common.Memory; | 1 | using Ryujinx.Common.Memory; |
2 | using Ryujinx.HLE.HOS.Services.Caps.Types; | 2 | using Ryujinx.HLE.HOS.Services.Caps.Types; |
3 | using SixLabors.ImageSharp; | 3 | using SkiaSharp; |
4 | using SixLabors.ImageSharp.PixelFormats; | ||
5 | using System; | 4 | using System; |
6 | using System.IO; | 5 | using System.IO; |
7 | using System.Runtime.CompilerServices; | 6 | using System.Runtime.CompilerServices; |
7 | using System.Runtime.InteropServices; | ||
8 | using System.Security.Cryptography; | 8 | using System.Security.Cryptography; |
9 | 9 | ||
10 | namespace Ryujinx.HLE.HOS.Services.Caps | 10 | namespace Ryujinx.HLE.HOS.Services.Caps |
@@ -118,7 +118,11 @@ namespace Ryujinx.HLE.HOS.Services.Caps | |||
118 | } | 118 | } |
119 | 119 | ||
120 | // NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data. | 120 | // NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data. |
121 | Image.LoadPixelData<Rgba32>(screenshotData, 1280, 720).SaveAsJpegAsync(filePath); | 121 | using var bitmap = new SKBitmap(new SKImageInfo(1280, 720, SKColorType.Rgba8888)); |
122 | Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), screenshotData.Length); | ||
123 | using var data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80); | ||
124 | using var file = File.OpenWrite(filePath); | ||
125 | data.SaveTo(file); | ||
122 | 126 | ||
123 | return ResultCode.Success; | 127 | return ResultCode.Success; |
124 | } | 128 | } |
diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj index 0fcf9e4b5..83a11d4e0 100644 --- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | <PropertyGroup> | 3 | <PropertyGroup> |
4 | <TargetFramework>net8.0</TargetFramework> | 4 | <TargetFramework>net8.0</TargetFramework> |
5 | <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
5 | </PropertyGroup> | 6 | </PropertyGroup> |
6 | 7 | ||
7 | <ItemGroup> | 8 | <ItemGroup> |
@@ -24,8 +25,8 @@ | |||
24 | <PackageReference Include="LibHac" /> | 25 | <PackageReference Include="LibHac" /> |
25 | <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" /> | 26 | <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" /> |
26 | <PackageReference Include="MsgPack.Cli" /> | 27 | <PackageReference Include="MsgPack.Cli" /> |
27 | <PackageReference Include="SixLabors.ImageSharp" /> | 28 | <PackageReference Include="SkiaSharp" /> |
28 | <PackageReference Include="SixLabors.ImageSharp.Drawing" /> | 29 | <PackageReference Include="SkiaSharp.NativeAssets.Linux" /> |
29 | <PackageReference Include="NetCoreServer" /> | 30 | <PackageReference Include="NetCoreServer" /> |
30 | </ItemGroup> | 31 | </ItemGroup> |
31 | 32 | ||
diff --git a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs index 58bdc90e6..1849f40cb 100644 --- a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs | |||
@@ -1,10 +1,7 @@ | |||
1 | using Ryujinx.Common; | 1 | using Ryujinx.Common; |
2 | using Ryujinx.Common.Configuration; | 2 | using Ryujinx.Common.Configuration; |
3 | using ShellLink; | 3 | using ShellLink; |
4 | using SixLabors.ImageSharp; | 4 | using SkiaSharp; |
5 | using SixLabors.ImageSharp.Formats.Png; | ||
6 | using SixLabors.ImageSharp.PixelFormats; | ||
7 | using SixLabors.ImageSharp.Processing; | ||
8 | using System; | 5 | using System; |
9 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
10 | using System.IO; | 7 | using System.IO; |
@@ -21,8 +18,8 @@ namespace Ryujinx.UI.Common.Helper | |||
21 | iconPath += ".ico"; | 18 | iconPath += ".ico"; |
22 | 19 | ||
23 | MemoryStream iconDataStream = new(iconData); | 20 | MemoryStream iconDataStream = new(iconData); |
24 | var image = Image.Load(iconDataStream); | 21 | using var image = SKBitmap.Decode(iconDataStream); |
25 | image.Mutate(x => x.Resize(128, 128)); | 22 | image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High); |
26 | SaveBitmapAsIcon(image, iconPath); | 23 | SaveBitmapAsIcon(image, iconPath); |
27 | 24 | ||
28 | var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0); | 25 | var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0); |
@@ -37,8 +34,10 @@ namespace Ryujinx.UI.Common.Helper | |||
37 | var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop"); | 34 | var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop"); |
38 | iconPath += ".png"; | 35 | iconPath += ".png"; |
39 | 36 | ||
40 | var image = Image.Load<Rgba32>(iconData); | 37 | var image = SKBitmap.Decode(iconData); |
41 | image.SaveAsPng(iconPath); | 38 | using var data = image.Encode(SKEncodedImageFormat.Png, 100); |
39 | using var file = File.OpenWrite(iconPath); | ||
40 | data.SaveTo(file); | ||
42 | 41 | ||
43 | using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop")); | 42 | using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop")); |
44 | outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}"); | 43 | outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}"); |
@@ -78,8 +77,10 @@ namespace Ryujinx.UI.Common.Helper | |||
78 | } | 77 | } |
79 | 78 | ||
80 | const string IconName = "icon.png"; | 79 | const string IconName = "icon.png"; |
81 | var image = Image.Load<Rgba32>(iconData); | 80 | var image = SKBitmap.Decode(iconData); |
82 | image.SaveAsPng(Path.Combine(resourceFolderPath, IconName)); | 81 | using var data = image.Encode(SKEncodedImageFormat.Png, 100); |
82 | using var file = File.OpenWrite(Path.Combine(resourceFolderPath, IconName)); | ||
83 | data.SaveTo(file); | ||
83 | 84 | ||
84 | // plist file | 85 | // plist file |
85 | using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist")); | 86 | using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist")); |
@@ -148,7 +149,7 @@ namespace Ryujinx.UI.Common.Helper | |||
148 | /// <param name="source">The source bitmap image that will be saved as an .ico file</param> | 149 | /// <param name="source">The source bitmap image that will be saved as an .ico file</param> |
149 | /// <param name="filePath">The location that the new .ico file will be saved too (Make sure to include '.ico' in the path).</param> | 150 | /// <param name="filePath">The location that the new .ico file will be saved too (Make sure to include '.ico' in the path).</param> |
150 | [SupportedOSPlatform("windows")] | 151 | [SupportedOSPlatform("windows")] |
151 | private static void SaveBitmapAsIcon(Image source, string filePath) | 152 | private static void SaveBitmapAsIcon(SKBitmap source, string filePath) |
152 | { | 153 | { |
153 | // Code Modified From https://stackoverflow.com/a/11448060/368354 by Benlitz | 154 | // Code Modified From https://stackoverflow.com/a/11448060/368354 by Benlitz |
154 | byte[] header = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 22, 0, 0, 0 }; | 155 | 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 | |||
156 | 157 | ||
157 | fs.Write(header); | 158 | fs.Write(header); |
158 | // Writing actual data | 159 | // Writing actual data |
159 | source.Save(fs, PngFormat.Instance); | 160 | using var data = source.Encode(SKEncodedImageFormat.Png, 100); |
161 | data.SaveTo(fs); | ||
160 | // Getting data length (file length minus header) | 162 | // Getting data length (file length minus header) |
161 | long dataLength = fs.Length - header.Length; | 163 | long dataLength = fs.Length - header.Length; |
162 | // Write it in the correct place | 164 | // Write it in the correct place |
163 | fs.Seek(14, SeekOrigin.Begin); | 165 | fs.Seek(14, SeekOrigin.Begin); |
164 | fs.WriteByte((byte)dataLength); | 166 | fs.WriteByte((byte)dataLength); |
165 | fs.WriteByte((byte)(dataLength >> 8)); | 167 | fs.WriteByte((byte)(dataLength >> 8)); |
168 | fs.WriteByte((byte)(dataLength >> 16)); | ||
169 | fs.WriteByte((byte)(dataLength >> 24)); | ||
166 | } | 170 | } |
167 | } | 171 | } |
168 | } | 172 | } |
diff --git a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs index 531d00611..0e7cfb8e6 100644 --- a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs | |||
@@ -41,17 +41,12 @@ namespace Ryujinx.Ava.UI.Applet | |||
41 | 41 | ||
42 | private void TextChanged(string text) | 42 | private void TextChanged(string text) |
43 | { | 43 | { |
44 | TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true); | 44 | TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false); |
45 | } | 45 | } |
46 | 46 | ||
47 | private void SelectionChanged(int selection) | 47 | private void SelectionChanged(int selection) |
48 | { | 48 | { |
49 | if (_hiddenTextBox.SelectionEnd < _hiddenTextBox.SelectionStart) | 49 | TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false); |
50 | { | ||
51 | _hiddenTextBox.SelectionStart = _hiddenTextBox.SelectionEnd; | ||
52 | } | ||
53 | |||
54 | TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true); | ||
55 | } | 50 | } |
56 | 51 | ||
57 | private void AvaloniaDynamicTextInputHandler_TextInput(object sender, string text) | 52 | 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 a055f3353..dd736037e 100644 --- a/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs +++ b/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs | |||
@@ -1,11 +1,14 @@ | |||
1 | using Avalonia.Controls; | 1 | using Avalonia.Controls; |
2 | using Avalonia.Input; | 2 | using Avalonia.Input; |
3 | using Avalonia.Interactivity; | 3 | using Avalonia.Interactivity; |
4 | using System; | ||
4 | 5 | ||
5 | namespace Ryujinx.Ava.UI.Helpers | 6 | namespace Ryujinx.Ava.UI.Helpers |
6 | { | 7 | { |
7 | public class OffscreenTextBox : TextBox | 8 | public class OffscreenTextBox : TextBox |
8 | { | 9 | { |
10 | protected override Type StyleKeyOverride => typeof(TextBox); | ||
11 | |||
9 | public static RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent() | 12 | public static RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent() |
10 | { | 13 | { |
11 | return KeyDownEvent; | 14 | return KeyDownEvent; |
diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml b/src/Ryujinx/UI/Windows/MainWindow.axaml index 6c2042f93..3a2e02c26 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml | |||
@@ -42,12 +42,10 @@ | |||
42 | </Window.KeyBindings> | 42 | </Window.KeyBindings> |
43 | <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> | 43 | <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> |
44 | <Grid.RowDefinitions> | 44 | <Grid.RowDefinitions> |
45 | <RowDefinition Height="Auto" /> | ||
46 | <RowDefinition Height="*" /> | 45 | <RowDefinition Height="*" /> |
47 | </Grid.RowDefinitions> | 46 | </Grid.RowDefinitions> |
48 | <helpers:OffscreenTextBox Name="HiddenTextBox" Grid.Row="0" /> | 47 | <helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" /> |
49 | <Grid | 48 | <Grid |
50 | Grid.Row="1" | ||
51 | HorizontalAlignment="Stretch" | 49 | HorizontalAlignment="Stretch" |
52 | VerticalAlignment="Stretch"> | 50 | VerticalAlignment="Stretch"> |
53 | <Grid.ColumnDefinitions> | 51 | <Grid.ColumnDefinitions> |