aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs')
-rw-r--r--Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs257
1 files changed, 257 insertions, 0 deletions
diff --git a/Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs b/Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs
new file mode 100644
index 00000000..e64c9da3
--- /dev/null
+++ b/Ryujinx.Profiler/UI/SharpFontHelpers/FontService.cs
@@ -0,0 +1,257 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using OpenTK;
+using OpenTK.Graphics.OpenGL;
+using SharpFont;
+
+namespace Ryujinx.Profiler.UI.SharpFontHelpers
+{
+ public class FontService
+ {
+ private struct CharacterInfo
+ {
+ public float Left;
+ public float Right;
+ public float Top;
+ public float Bottom;
+
+ public int Width;
+ public float Height;
+
+ public float AspectRatio;
+
+ public float BearingX;
+ public float BearingY;
+ public float Advance;
+ }
+
+ private const int SheetWidth = 1024;
+ private const int SheetHeight = 512;
+ private int ScreenWidth, ScreenHeight;
+ private int CharacterTextureSheet;
+ private CharacterInfo[] characters;
+
+ public Color fontColor { get; set; } = Color.Black;
+
+ private string GetFontPath()
+ {
+ string fontFolder = System.Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
+
+ // Only uses Arial, add more fonts here if wanted
+ string path = Path.Combine(fontFolder, "arial.ttf");
+ if (File.Exists(path))
+ {
+ return path;
+ }
+
+ throw new Exception($"Profiler exception. Required font Courier New or Arial not installed to {fontFolder}");
+ }
+
+ public void InitalizeTextures()
+ {
+ // Create and init some vars
+ uint[] rawCharacterSheet = new uint[SheetWidth * SheetHeight];
+ int x;
+ int y;
+ int lineOffset;
+ int maxHeight;
+
+ x = y = lineOffset = maxHeight = 0;
+ characters = new CharacterInfo[94];
+
+ // Get font
+ var font = new FontFace(File.OpenRead(GetFontPath()));
+
+ // Update raw data for each character
+ for (int i = 0; i < 94; i++)
+ {
+ var surface = RenderSurface((char)(i + 33), font, out var xBearing, out var yBearing, out var advance);
+
+ characters[i] = UpdateTexture(surface, ref rawCharacterSheet, ref x, ref y, ref lineOffset);
+ characters[i].BearingX = xBearing;
+ characters[i].BearingY = yBearing;
+ characters[i].Advance = advance;
+
+ if (maxHeight < characters[i].Height)
+ maxHeight = (int)characters[i].Height;
+ }
+
+ // Fix height for characters shorter than line height
+ for (int i = 0; i < 94; i++)
+ {
+ characters[i].BearingX /= characters[i].Width;
+ characters[i].BearingY /= maxHeight;
+ characters[i].Advance /= characters[i].Width;
+ characters[i].Height /= maxHeight;
+ characters[i].AspectRatio = (float)characters[i].Width / maxHeight;
+ }
+
+ // Convert raw data into texture
+ CharacterTextureSheet = GL.GenTexture();
+ GL.BindTexture(TextureTarget.Texture2D, CharacterTextureSheet);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Clamp);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Clamp);
+
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, SheetWidth, SheetHeight, 0, PixelFormat.Rgba, PixelType.UnsignedInt8888, rawCharacterSheet);
+
+ GL.BindTexture(TextureTarget.Texture2D, 0);
+ }
+
+ public void UpdateScreenHeight(int height)
+ {
+ ScreenHeight = height;
+ }
+
+ public float DrawText(string text, float x, float y, float height, bool draw = true)
+ {
+ float originalX = x;
+
+ // Skip out of bounds draw
+ if (y < height * -2 || y > ScreenHeight + height * 2)
+ {
+ draw = false;
+ }
+
+ if (draw)
+ {
+ // Use font map texture
+ GL.BindTexture(TextureTarget.Texture2D, CharacterTextureSheet);
+
+ // Enable blending and textures
+ GL.Enable(EnableCap.Texture2D);
+ GL.Enable(EnableCap.Blend);
+ GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
+
+ // Draw all characters
+ GL.Begin(PrimitiveType.Triangles);
+ GL.Color4(fontColor);
+ }
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (text[i] == ' ')
+ {
+ x += height / 4;
+ continue;
+ }
+
+ CharacterInfo charInfo = characters[text[i] - 33];
+ float width = (charInfo.AspectRatio * height);
+ x += (charInfo.BearingX * charInfo.AspectRatio) * width;
+ float right = x + width;
+ if (draw)
+ {
+ DrawChar(charInfo, x, right, y + height * (charInfo.Height - charInfo.BearingY), y - height * charInfo.BearingY);
+ }
+ x = right + charInfo.Advance * charInfo.AspectRatio + 1;
+ }
+
+ if (draw)
+ {
+ GL.End();
+
+ // Cleanup for caller
+ GL.BindTexture(TextureTarget.Texture2D, 0);
+ GL.Disable(EnableCap.Texture2D);
+ GL.Disable(EnableCap.Blend);
+ }
+
+ // Return width of rendered text
+ return x - originalX;
+ }
+
+ private void DrawChar(CharacterInfo charInfo, float left, float right, float top, float bottom)
+ {
+ GL.TexCoord2(charInfo.Left, charInfo.Bottom); GL.Vertex2(left, bottom);
+ GL.TexCoord2(charInfo.Left, charInfo.Top); GL.Vertex2(left, top);
+ GL.TexCoord2(charInfo.Right, charInfo.Top); GL.Vertex2(right, top);
+
+ GL.TexCoord2(charInfo.Right, charInfo.Top); GL.Vertex2(right, top);
+ GL.TexCoord2(charInfo.Right, charInfo.Bottom); GL.Vertex2(right, bottom);
+ GL.TexCoord2(charInfo.Left, charInfo.Bottom); GL.Vertex2(left, bottom);
+ }
+
+ public unsafe Surface RenderSurface(char c, FontFace font, out float xBearing, out float yBearing, out float advance)
+ {
+ var glyph = font.GetGlyph(c, 64);
+ xBearing = glyph.HorizontalMetrics.Bearing.X;
+ yBearing = glyph.RenderHeight - glyph.HorizontalMetrics.Bearing.Y;
+ advance = glyph.HorizontalMetrics.Advance;
+
+ var surface = new Surface
+ {
+ Bits = Marshal.AllocHGlobal(glyph.RenderWidth * glyph.RenderHeight),
+ Width = glyph.RenderWidth,
+ Height = glyph.RenderHeight,
+ Pitch = glyph.RenderWidth
+ };
+
+ var stuff = (byte*)surface.Bits;
+ for (int i = 0; i < surface.Width * surface.Height; i++)
+ *stuff++ = 0;
+
+ glyph.RenderTo(surface);
+
+ return surface;
+ }
+
+ private CharacterInfo UpdateTexture(Surface surface, ref uint[] rawCharMap, ref int posX, ref int posY, ref int lineOffset)
+ {
+ int width = surface.Width;
+ int height = surface.Height;
+ int len = width * height;
+ byte[] data = new byte[len];
+
+ // Get character bitmap
+ Marshal.Copy(surface.Bits, data, 0, len);
+
+ // Find a slot
+ if (posX + width > SheetWidth)
+ {
+ posX = 0;
+ posY += lineOffset;
+ lineOffset = 0;
+ }
+
+ // Update lineoffset
+ if (lineOffset < height)
+ {
+ lineOffset = height + 1;
+ }
+
+ // Copy char to sheet
+ for (int y = 0; y < height; y++)
+ {
+ int destOffset = (y + posY) * SheetWidth + posX;
+ int sourceOffset = y * width;
+
+ for (int x = 0; x < width; x++)
+ {
+ rawCharMap[destOffset + x] = (uint)((0xFFFFFF << 8) | data[sourceOffset + x]);
+ }
+ }
+
+ // Generate character info
+ CharacterInfo charInfo = new CharacterInfo()
+ {
+ Left = (float)posX / SheetWidth,
+ Right = (float)(posX + width) / SheetWidth,
+ Top = (float)(posY - 1) / SheetHeight,
+ Bottom = (float)(posY + height) / SheetHeight,
+ Width = width,
+ Height = height,
+ };
+
+ // Update x
+ posX += width + 1;
+
+ // Give the memory back
+ Marshal.FreeHGlobal(surface.Bits);
+ return charInfo;
+ }
+ }
+} \ No newline at end of file