aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs')
-rw-r--r--src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs390
1 files changed, 209 insertions, 181 deletions
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 @@
1using Ryujinx.HLE.UI; 1using Ryujinx.HLE.UI;
2using Ryujinx.Memory; 2using Ryujinx.Memory;
3using SixLabors.Fonts; 3using SkiaSharp;
4using SixLabors.ImageSharp;
5using SixLabors.ImageSharp.Drawing.Processing;
6using SixLabors.ImageSharp.PixelFormats;
7using SixLabors.ImageSharp.Processing;
8using System; 4using System;
9using System.Diagnostics; 5using System.Diagnostics;
10using System.IO; 6using System.IO;
11using System.Numerics;
12using System.Reflection; 7using System.Reflection;
13using System.Runtime.InteropServices; 8using 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