#pragma warning disable CA1416 using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; using System.IO; using System.Numerics; using System.Text; using System.Text.Json; using OpenGLTutorial.Rendering.Display; using static OpenGL.GL; namespace OpenGLTutorial.Rendering.Objects.Components { [Serializable] public class TextSettings { public int GlyphsPerLine { get; set; } = 16; public int GlyphLineCount { get; set; } = 16; public int GlyphWidth { get; set; } = 30; public int GlyphHeight { get; set; } = 48; public float CharXSpacing { get; set; } = 0.0f; // Used to offset rendering glyphs to bitmap public int AtlasOffsetX { get; set; } = -3; public int AtlasOffsetY { get; set; } = -1; public int FontSize { get; set; } = 35; public bool BitmapFont { get; set; } = false; public float TexCoordOffset { get; set; } = 0.005f; public void Save(string file) { string json = JsonSerializer.Serialize(this, new JsonSerializerOptions {WriteIndented = true}); File.WriteAllText(file, json, Encoding.UTF8); } public static TextSettings Load(string file) { string text = File.ReadAllText(file, Encoding.UTF8); return JsonSerializer.Deserialize(text); } } public class TextComponent : IComponent { public static TextSettings CompileFont(string fontNameOrUrl, string output, TextSettings settings = null) { if (settings is null) settings = new TextSettings(); int bitmapWidth = settings.GlyphsPerLine * settings.GlyphWidth; int bitmapHeight = settings.GlyphLineCount * settings.GlyphHeight; using Bitmap texture = new Bitmap(bitmapWidth, bitmapHeight, PixelFormat.Format32bppArgb); Font font; if (File.Exists(fontNameOrUrl)) { var collection = new PrivateFontCollection(); collection.AddFontFile(fontNameOrUrl); var fontFamily = new FontFamily(Path.GetFileNameWithoutExtension(fontNameOrUrl), collection); font = new Font(fontFamily, settings.FontSize); } else { font = new Font(new FontFamily(fontNameOrUrl), settings.FontSize); } using (var g = Graphics.FromImage(texture)) { if (settings.BitmapFont) { g.SmoothingMode = SmoothingMode.None; g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel; } else { g.SmoothingMode = SmoothingMode.HighQuality; g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; } for (int p = 0; p < settings.GlyphLineCount; p++) { for (int n = 0; n < settings.GlyphsPerLine; n++) { char c = (char)(n + p * settings.GlyphsPerLine); g.DrawString(c.ToString(), font, Brushes.White, n * settings.GlyphWidth + settings.AtlasOffsetX, p * settings.GlyphHeight + settings.AtlasOffsetY); } } } texture.Save(output); return settings; } public string Text { get; set; } public Vector2 Size { get; private set; } public Color TextColor { get; set; } = Color.White; private Shape Shape { get; set; } private Texture Texture { get; set; } private TextSettings Settings { get; set; } private string CurrentText { get; set; } public TextComponent(string fontImage, string text, TextSettings settings = null, int renderingMode = GL_STATIC_DRAW) { Texture = new Texture(fontImage); Shape = new Shape(Shapes.Rectangle, renderingMode); Settings = settings is not null ? settings : new TextSettings(); } public void Load(GameObject thisObject) { Texture.Load(); Shape.Load(); } public void Update(GameObject thisObject) { if (CurrentText == Text) return; CurrentText = Text; List vertices = new List(); float x = 0; float y = 0; float uW = Settings.GlyphWidth / (float)Texture.Width; float vH = Settings.GlyphHeight / (float)Texture.Height; float w = Settings.GlyphWidth / (float)Settings.GlyphHeight; const float h = 1.0f; for (int n = 0; n < Text.Length; n++) { char idx = Text[n]; if (idx == '\n') { y += 1; x = 0; continue; } if (idx == '\t') { x += (w + Settings.CharXSpacing) * 4; continue; } float u = (idx % Settings.GlyphsPerLine) * uW + Settings.TexCoordOffset; float v = (idx / Settings.GlyphsPerLine) * vH + Settings.TexCoordOffset; vertices.AddRange(new[] { x, y + h, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u, v + vH, x + w, y + h, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u + uW, v + vH, x, y, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u, v, x + w, y, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u + uW, v, x + w, y + h, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u + uW, v + vH, x, y, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u, v }); x += w + Settings.CharXSpacing; } Shape.SetVertices(vertices.ToArray()); Size = new Vector2(x, y); } public void Destroy(GameObject thisObject) { Shape.Delete(); Texture.Delete(); } public void Render(GameObject thisObject) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Texture.Use(); glBindVertexArray(Shape.Vao); glDrawArrays(GL_TRIANGLES, 0, Shape.GetVertexCount()); glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } } }