private static IEnumerable <FontInfo> AddFontFileToCache(Dictionary <string, List <FontInfo> > cache, Dictionary <string, FontInfo> fullCache, string path) { FontInfo AddFont(OpenTypeFont font) { #if DEBUG Logger.log.Debug($"'{path}' = '{font.Family}' '{font.Subfamily}' ({font.FullName})"); #endif var fontInfo = new FontInfo(path, font); var list = GetListForFamily(cache, font.Family); list.Add(fontInfo); var name = font.FullName; if (fullCache.ContainsKey(name)) { // Beat Saber 1.13.4 includes well over 100+ fonts that most systems have, this completely blows up the console on game launch. // Logger.log.Warn($"Duplcicate font with full name '{name}' at {path}"); } else { fullCache.Add(name, fontInfo); } return(fontInfo); } using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); using var reader = OpenTypeReader.For(fileStream); if (reader is OpenTypeCollectionReader colReader) { var collection = new OpenTypeCollection(colReader, lazyLoad: false); return(collection.Select(AddFont).ToList()); } else if (reader is OpenTypeFontReader fontReader) { var font = new OpenTypeFont(fontReader, lazyLoad: false); return(Utilities.SingleEnumerable(AddFont(font))); } else { Logger.log.Warn($"Font file '{path}' is not an OpenType file"); return(Enumerable.Empty <FontInfo>()); } }
internal void Launch() { Console.WriteLine("Bezier..."); Console.WriteLine(" 'i': Image."); Console.WriteLine(" 's': SVG tiger."); Console.WriteLine(" 't': Text."); using var windowServer = new WindowServer(); using var renderingContext = new RenderingContext { Settings = new RenderingSettings { Samples = 8 } }; using var window = windowServer.CreateWindow(renderingContext); window.Title = "Bézier"; window.BorderStyle = WindowBorderStyle.Sizable; window.Size = (900, 900); renderingContext.CurrentWindow = window; var viewportSize = window.ViewportSize; var windowSize = window.Size; var backend = new DrawingBackend(new EntryPointLoader(renderingContext)); var openTypeCollection = new OpenTypeCollection(); var nameId = typeof(VectorGraphicsLauncher).Namespace + ".Resources.ebrima.ttf"; openTypeCollection.AddFontResource(nameId, () => Assembly.GetExecutingAssembly().GetManifestResourceStream(nameId) !); using var sharedContext = new SharedDrawingContext(backend, openTypeCollection); using var drawingContext = new DrawingContext(sharedContext) { ClearColor = new RgbaColor(155, 155, 155, 255) }; var redrawRequired = true; var images = new List <ImageDrawing>(); var paths = new List <PathDrawing>(); var labels = new List <LabelDrawing>(); window.TextInput += (sender, e) => { switch (e.CodePoint) { case 'i': { var image = new RasterImage(); var namePrefix = typeof(DrawTextureLauncher).Namespace + ".Resources."; using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(namePrefix + "kitten.png")) { image.LoadPng(stream ?? throw new InvalidOperationException()); } var imageDrawing = new ImageDrawing() { Image = image }; images.Add(imageDrawing); redrawRequired = true; } break; case 's': { images.Clear(); paths.Clear(); labels.Clear(); var sw = new System.Diagnostics.Stopwatch(); sw.Start(); var pathReader = new SvgPathReader(); var transform = new AffineTransform(m11: 1.76, m22: 1.76, m31: 325, m32: 255); var namePrefix = typeof(VectorGraphicsLauncher).Namespace + ".Resources."; XDocument document; using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(namePrefix + "Ghostscript_Tiger.svg")) { document = XDocument.Load(stream); } XNamespace ns = "http://www.w3.org/2000/svg"; var rootG = document.Element(ns + "svg").Element(ns + "g"); foreach (var g in rootG.Elements(ns + "g")) { var path = new PathDrawing { Transform = transform }; paths.Add(path); foreach (var a in g.Attributes()) { switch (a.Name.LocalName) { case "fill": path.FillColor = ParseSvgColor(a.Value); break; case "stroke": path.StrokeColor = ParseSvgColor(a.Value); break; case "stroke-width": path.StrokeWidth = float.Parse(a.Value); break; } } pathReader.PathData = g.Element(ns + "path").Attribute("d").Value; var x = 0f; var y = 0f; var initialX = 0f; var initialY = 0f; (double x, double y)c2 = (0, 0); bool hasPreviousC2 = false; while (pathReader.Read()) { switch (pathReader.PathCommand) { case PathCommandType.MoveTo: x = pathReader.X; y = pathReader.Y; path.Move((x, y)); initialX = x; initialY = y; hasPreviousC2 = false; break; case PathCommandType.MoveToRelative: x += pathReader.X; y += pathReader.Y; path.Move((x, y)); initialX = x; initialY = y; hasPreviousC2 = false; break; case PathCommandType.LineToRelative: x += pathReader.X; y += pathReader.Y; path.AddLine((x, y)); hasPreviousC2 = false; break; case PathCommandType.CurveToRelative: { var c1 = ((double)x + pathReader.X1, (double)y + pathReader.Y1); c2 = (x + pathReader.X2, y + pathReader.Y2); x += pathReader.X; y += pathReader.Y; path.AddCubicBezier(c1, c2, (x, y)); hasPreviousC2 = true; } break; case PathCommandType.SmoothCurveToRelative: { (double x, double y)c1; if (hasPreviousC2) { c1 = (2 * x - c2.x, 2 * y - c2.y); } else { c1 = (x, y); } c2 = (x + pathReader.X2, y + pathReader.Y2); x += pathReader.X; y += pathReader.Y; path.AddCubicBezier(c1, c2, (x, y)); hasPreviousC2 = true; } break; case PathCommandType.VerticalLineToRelative: y += pathReader.Y; path.AddLine((x, y)); hasPreviousC2 = false; break; case PathCommandType.Closepath: path.AddLine((initialX, initialY)); hasPreviousC2 = false; break; default: Console.WriteLine("Unhandled command: " + pathReader.PathCommand); break; } } } sw.Stop(); Console.WriteLine($"SVG file decoded in {sw.ElapsedMilliseconds}ms ({paths.Count} paths)."); redrawRequired = true; } break; case 't': { images.Clear(); paths.Clear(); labels.Clear(); var label = new LabelDrawing { Transform = new AffineTransform(m11: 4, m22: 4, m31: 20, m32: 50), FontFamily = "Ebrima", FontSubfamily = FontSubfamily.Normal, FontPoints = 8 }; label.AppendString("The quick brown fox jumps over the lazy dog."); labels.Add(label); for (int i = 0; i < 15; i++) { label = new LabelDrawing { Transform = new AffineTransform(m11: 1, m22: 1, m31: 20, m32: 100 + i * 30), FontFamily = "Ebrima", FontSubfamily = FontSubfamily.Normal, FontPoints = 32 - i * 2 }; label.AppendString("The quick brown fox jumps over the lazy dog."); labels.Add(label); } redrawRequired = true; } break; } }; void draw() { var sw = new System.Diagnostics.Stopwatch(); sw.Start(); renderingContext.CurrentWindow = window; drawingContext.SharedContext.PixelScaling = viewportSize.height / windowSize.height; drawingContext.StartDrawing(); foreach (var image in images) { image.Draw(drawingContext); } foreach (var path in paths) { path.Draw(drawingContext); } foreach (var label in labels) { label.Draw(drawingContext, 0, 0); } drawingContext.FinishDrawing(); renderingContext.SwapBuffers(window); sw.Stop(); Console.WriteLine($"Drawn in {sw.ElapsedMilliseconds}ms."); } void resize() { viewportSize = window.ViewportSize; windowSize = window.Size; backend.WindowSize = windowSize; } window.Resize += (sender, e) => { resize(); draw(); }; var runLoop = MainRunLoop.Create(windowServer); window.Closed += (sender, e) => { drawingContext.Dispose(); backend.Dispose(); runLoop.Interrupt(); }; resize(); runLoop.RecurringActions.Add((delay) => { if (redrawRequired) { draw(); redrawRequired = false; } }); window.Visible = true; runLoop.Run(); Console.WriteLine("Bezier done."); }
internal void Launch() { Console.WriteLine("Text Area..."); using var windowServer = new WindowServer(); using var renderingContext = new RenderingContext { Settings = new RenderingSettings { Samples = 8 } }; using var window = windowServer.CreateWindow(renderingContext); window.Title = "Text Area"; window.BorderStyle = WindowBorderStyle.Sizable; window.Size = (900, 900); renderingContext.CurrentWindow = window; var viewportSize = window.ViewportSize; var windowSize = window.Size; var backend = new DrawingBackend(new EntryPointLoader(renderingContext)) { PixelScaling = viewportSize.height / windowSize.height }; var openTypeCollection = new OpenTypeCollection(); var nameId = typeof(VectorGraphicsLauncher).Namespace + ".Resources.Hack-Regular.ttf"; openTypeCollection.AddFontResource(nameId, () => Assembly.GetExecutingAssembly().GetManifestResourceStream(nameId)!); using var sharedContext = new SharedDrawingContext(backend, openTypeCollection); using var drawingContext = new DrawingContext(sharedContext) { ClearColor = new RgbaColor(35, 35, 35, 255) }; var styles = new MonospaceTextStyles(256); var fontSubfamily = openTypeCollection.LookupFontSubfamily("Hack", FontSubfamily.Normal); styles.GetStyle(fontSubfamily, new RgbaColor(255, 255, 255, 255), new RgbaColor(0, 0, 0, 0)); var textArea = new TextArea(styles, "Hack", fontSubfamily, 9); var path = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, "..", "..", "..", "TextAreaLauncher.cs"); textArea.Load(path); const int textLeft = 20; const int textTop = 30; window.Scroll += (sender, e) => { textArea.ScrollVertically(e.DeltaY * 30); }; var hideCaret = true; window.KeyDown += (sender, e) => { switch (e.Keycode) { case Keycode.Up: textArea.MoveUp(); hideCaret = false; break; case Keycode.Down: textArea.MoveDown(); hideCaret = false; break; case Keycode.Left: textArea.MoveLeft(); hideCaret = false; break; case Keycode.Right: textArea.MoveRight(); hideCaret = false; break; case Keycode.F11: textArea.FontPoints--; break; case Keycode.F12: textArea.FontPoints++; break; case Keycode.Backspace: textArea.Backspace(); hideCaret = false; break; case Keycode.Tab: textArea.Tab(); hideCaret = false; break; case Keycode.Enter: textArea.Enter(); hideCaret = false; break; } }; window.TextInput += (sender, e) => { textArea.Insert(e.CodePoint); hideCaret = false; }; window.MouseDown += (sender, e) => { if (e.ChangedButton == MouseButton.Left) { var (x, y) = window.CursorPosition; textArea.LeftMouseDown(x, y); hideCaret = false; } }; var interruptThread = false; void draw() { renderingContext.CurrentWindow = window; drawingContext.SharedContext.PixelScaling = viewportSize.height / windowSize.height; drawingContext.StartDrawing(); textArea.Render(drawingContext); drawingContext.FinishDrawing(); renderingContext.SwapBuffers(window); } void resize() { viewportSize = window.ViewportSize; windowSize = window.Size; renderingContext.CurrentWindow = window; backend.WindowSize = windowSize; textArea.SetViewRectangle(textLeft, textTop, (windowSize.width - textLeft - 30), (windowSize.height - textTop - 50)); } window.Resize += (sender, e) => { resize(); draw(); }; var runLoop = MainRunLoop.Create(windowServer); var text = default(DecoratedText<byte>); var version = 0; var th = new Thread(() => { var regexList = new List<Regex>(); var styleList = new List<MonospaceTextStyle>(); regexList.Add(new Regex("\\b(break|case|class|const|else|false|float|for|foreach|" + "if|int|internal|public|namespace|new|unsafe|using|string|switch|true|var|void)\\b")); styleList.Add(styles.GetStyle(fontSubfamily, new RgbaColor(81, 153, 213, 255), new RgbaColor(0, 0, 0, 0))); regexList.Add(new Regex(@"\b-?\d+(,\d+)*(\.\d+((e|E)\d+)?)?\b")); styleList.Add(styles.GetStyle(fontSubfamily, new RgbaColor(0, 0, 0, 255), new RgbaColor(230, 180, 50, 255))); regexList.Add(new Regex(@"""(\\.|[^""\\])*""")); styleList.Add(styles.GetStyle(fontSubfamily, new RgbaColor(250, 180, 230, 255), new RgbaColor(0, 0, 0, 0))); while (!interruptThread) { var t = text; text = null; var v = version; if (t != null) { var txt = new StyledMonospaceText(t, styles); var builder = new StringBuilder(); for (int i = 0; i < txt.LineCount; i++) { builder.Clear(); txt.CopyLineTo(i, builder); var str = builder.ToString(); for (int j = 0; j < regexList.Count; j++) { var m = regexList[j].Match(str); var s = styleList[j]; while (m.Success) { txt.StyleRange(s, (i, m.Index), (i, m.Index + m.Length)); m = m.NextMatch(); } } } void f(int currentVersion, StyledMonospaceText newText) { runLoop.InvokeLater(0, (lag) => { if (textArea.Version == currentVersion) { textArea.SetText(newText); } }); } f(v, txt); } Thread.Sleep(500); } }); void blink(long lag) { textArea.HideCaret = hideCaret; hideCaret = !hideCaret; if (textArea.RequireRestyle) { text = textArea.GetText(); version = textArea.Version; } runLoop.InvokeLater(500, blink); } runLoop.InvokeLater(500, blink); window.Closed += (sender, e) => { backend.Dispose(); runLoop.Interrupt(); interruptThread = true; }; resize(); runLoop.RecurringActions.Add((time) => { if (textArea.RequireRender) { draw(); } }); th.Start(); window.Visible = true; runLoop.Run(); Console.WriteLine("Text Area done."); }