From 78faf4cb81423d602fbf60bea61dac8d63293dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sat, 8 Jan 2022 02:17:35 +0100 Subject: [PATCH] Simple bidirectional text --- .../src/Renderer/Text/FontRenderer.bf | 107 +++++++++++++----- Sandbox/change_direction_test.txt | 3 +- Sandbox/src/ExampleLayer2D.bf | 10 +- Sandbox/test.txt | 4 + 4 files changed, 89 insertions(+), 35 deletions(-) diff --git a/GlitchyEngine/src/Renderer/Text/FontRenderer.bf b/GlitchyEngine/src/Renderer/Text/FontRenderer.bf index 9c6da9b..6453ba4 100644 --- a/GlitchyEngine/src/Renderer/Text/FontRenderer.bf +++ b/GlitchyEngine/src/Renderer/Text/FontRenderer.bf @@ -107,7 +107,15 @@ namespace GlitchyEngine.Renderer.Text } } - public static PreparedText PrepareText(Font font, String text, float fontSize, Color fontColor = .White, Color bitmapColor = .White, float lineSpaceScale = 1.0f) + public enum TextDirection + { + case LeftToRight; + case RightToLeft; + case TopToBottom; + case BottomToTop; + } + + public static PreparedText PrepareText(Font font, String text, float fontSize, Color fontColor = .White, Color bitmapColor = .White, float lineSpaceScale = 1.0f, TextDirection direction = .LeftToRight) { if(text.IsWhiteSpace) return .Empty; @@ -132,8 +140,20 @@ namespace GlitchyEngine.Renderer.Text //PreparedLine currentLine = new PreparedLine(); - void FlushShapeBuffer(Font font, float scale) + // Stack to keep track of the text direction. + List textDirectionStack = scope .(); + textDirectionStack.Add(direction); + + int movedLines = 0; + + Font currentFont = font; + + int currentIndex = 0; + + void FlushShapeBuffer() { + float fontScale = (float)fontSize / currentFont._fontSize; + hb_buffer_clear_contents(buf); hb_buffer_add_utf8(buf, text.Ptr, (.)text.Length, (.)(textBuffer.Ptr - text.Ptr), (.)textBuffer.Length); @@ -143,7 +163,21 @@ namespace GlitchyEngine.Renderer.Text // Set the script, language and direction of the buffer. // TODO: change direction, script, etc. - hb_buffer_set_direction(buf, .HB_DIRECTION_LTR); + + TextDirection direction = textDirectionStack.Back; + + switch (direction) + { + case .LeftToRight: + hb_buffer_set_direction(buf, .HB_DIRECTION_LTR); + case .RightToLeft: + hb_buffer_set_direction(buf, .HB_DIRECTION_RTL); + case .TopToBottom: + hb_buffer_set_direction(buf, .HB_DIRECTION_TTB); + case .BottomToTop: + hb_buffer_set_direction(buf, .HB_DIRECTION_BTT); + } + hb_buffer_set_script(buf, .HB_SCRIPT_LATIN); hb_buffer_set_language(buf, hb_language_from_string("en".CStr(), -1)); @@ -163,54 +197,73 @@ namespace GlitchyEngine.Renderer.Text hb_position_t x_advance = glyph_pos[i].x_advance; hb_position_t y_advance = glyph_pos[i].y_advance; - PreparedGlyph glyph = .(font, glyphid, .(penPosition, baseline), scale);//, x_advance / 64, y_advance / 64); + PreparedGlyph glyph = .(font, glyphid, .(penPosition, baseline), fontScale);//, x_advance / 64, y_advance / 64); preparedText.Glyphs.Add(glyph); - penPosition += (x_advance / 64) * scale; - baseline += (y_advance / 64) * scale; + penPosition += (x_advance / 64) * fontScale; + baseline += (y_advance / 64) * fontScale; } } - int movedLines = 0; - - bool flush = false; - - Font currentFont = font; - - int currentIndex = 0; - for (char32 char in text.DecodedChars) { - // TODO: right to left mark (/ embedding?) - // TODO: left to right mark (/ embedding?) + defer + { + currentIndex = @char.NextIndex; + } + switch(char) { case '\n': + // Only flush after the first new line + if (movedLines == 0) + FlushShapeBuffer(); + // carriage return movedLines++; - flush = true; - currentIndex = @char.NextIndex; + continue; case '\u{240}': + FlushShapeBuffer(); + // carriage return penPosition = 0; - flush = true; - currentIndex = @char.NextIndex; + + continue; + case '\u{2066}': + // LEFT-TO-RIGHT ISOLATE + FlushShapeBuffer(); + + textDirectionStack.Add(.RightToLeft); + + continue; + case '\u{2067}': + // RIGHT-TO-LEFT ISOLATE + FlushShapeBuffer(); + + textDirectionStack.Add(.RightToLeft); + + continue; + // TODO: FIRST STRONG ISOLATE (U+2068) + case '\u{2069}': + // POP DIRECTIONAL ISOLATE + FlushShapeBuffer(); + + if (textDirectionStack.Count > 1) + textDirectionStack.PopBack(); + continue; } // Get the font that can draw the char. Use the given font if no fallback is found (will draw the "missing glyph"). Font charFont = Font.GetDrawingFont(char, font) ?? font; - if (flush || charFont != font) + if (charFont != font) { - float fontScale = (float)fontSize / charFont._fontSize; - - FlushShapeBuffer(currentFont, fontScale); + FlushShapeBuffer(); currentFont = charFont; - flush = true; } // move baseline if necessary @@ -232,11 +285,9 @@ namespace GlitchyEngine.Renderer.Text textBuffer.Ptr = text.Ptr + currentIndex; } textBuffer.Length += @char.NextIndex - currentIndex; - currentIndex = @char.NextIndex; } - float fontScale = (float)fontSize / currentFont._fontSize; - FlushShapeBuffer(currentFont, fontScale); + FlushShapeBuffer(); return preparedText; } diff --git a/Sandbox/change_direction_test.txt b/Sandbox/change_direction_test.txt index f884d3f..93328a6 100644 --- a/Sandbox/change_direction_test.txt +++ b/Sandbox/change_direction_test.txt @@ -1,2 +1 @@ -I enjoyed staying -- באמת! -- at his house. -I enjoyed staying -- באמת!‏ -- at his house. \ No newline at end of file +Hallo⁧Hallo⁩Hallo \ No newline at end of file diff --git a/Sandbox/src/ExampleLayer2D.bf b/Sandbox/src/ExampleLayer2D.bf index d2aaab1..42ecb14 100644 --- a/Sandbox/src/ExampleLayer2D.bf +++ b/Sandbox/src/ExampleLayer2D.bf @@ -87,7 +87,7 @@ namespace Sandbox }; _depthStencilState = new DepthStencilState(dssDesc); - File.ReadAllText("lorem.txt", text); + File.ReadAllText("change_direction_test.txt", text); prepText = FontRenderer.PrepareText(fonty, text, 64, .White, .White); } @@ -134,13 +134,13 @@ namespace Sandbox //FontRenderer.DrawText(fonty, text, 0, 0, 64, .White, .White); //var prepared = FontRenderer.PrepareText(fonty,"Hallö!\n gjy ÄA\n\nwww www <--||-->", 64, .White, .White); - //var prepared = FontRenderer.PrepareText(fonty, text, 64, .White, .White); + var prepared = FontRenderer.PrepareText(fonty, text, 64, .White, .White); //var prepared = FontRenderer.PrepareText(fonty,"A\nB", 64, .White, .White); - //FontRenderer.DrawText(prepared, 0, 0); - FontRenderer.DrawText(prepText, 0, 0); + FontRenderer.DrawText(prepared, 0, 0); + //FontRenderer.DrawText(prepText, 0, 0); - //prepared.ReleaseRef(); + prepared.ReleaseRef(); Renderer2D.EndScene(); } diff --git a/Sandbox/test.txt b/Sandbox/test.txt index 83fa6af..0a7f0c7 100644 --- a/Sandbox/test.txt +++ b/Sandbox/test.txt @@ -4,3 +4,7 @@ Mir geht es gut, danke der Nachfrage. Wie geht es dir? ÄÖÜüöäßÉéáÁèÈÀàâÊ💕❤ λ≙Ω⨁μ×∞ 🤦🏿‍♀️ + + +I enjoyed staying -- באמת! -- at his house. +I enjoyed staying -- באמת!‏ -- at his house. \ No newline at end of file