changeset 1417:23b4d8c84501

fix: small offset error with left-bearing when aligning characters
author sam <sam@basx.dev>
date Wed, 01 Jan 2025 16:38:27 +0700
parents a4fd9c31a225
children 5bbcd40eb145
files semicongine/text.nim semicongine/text/font.nim
diffstat 2 files changed, 21 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/text.nim	Wed Jan 01 12:42:43 2025 +0700
+++ b/semicongine/text.nim	Wed Jan 01 16:38:27 2025 +0700
@@ -68,6 +68,7 @@
   FontObj*[MaxGlyphs: static int] = object
     advance*: Table[Rune, float32]
     kerning*: Table[(Rune, Rune), float32]
+    leftBearing*: Table[Rune, float32]
     lineAdvance*: float32
     lineHeight*: float32 # like lineAdvance - lineGap
     ascent*: float32 # from baseline to highest glyph
@@ -168,8 +169,12 @@
 proc textDimension*(font: Font, text: string): Vec2f =
   textDimension(font, text.toRunes())
 
+proc textDimension*(textBuffer: TextBuffer, text: string | seq[Rune]): Vec2f =
+  textDimension(textBuffer.font, text) * textBuffer.baseScale
+
 proc updateGlyphData*(textbuffer: var TextBuffer, textHandle: TextHandle) =
   assert textHandle.generation == textbuffer.generation
+
   let
     textI = textHandle.index
     text = textbuffer.texts[textI].text
@@ -177,7 +182,7 @@
     anchor = textbuffer.texts[textI].anchor
 
     globalScale = textbuffer.texts[textI].scale * textbuffer.baseScale
-    box = textDimension(textbuffer.font, text) * globalScale
+    box = textbuffer.textDimension(text) * textbuffer.texts[textI].scale
     xH = textbuffer.font.xHeight * globalScale
     aratio = getAspectRatio()
     origin = vec3(
@@ -189,6 +194,9 @@
     lineWidths = splitLines(text).toSeq.mapIt(width(textbuffer.font, it) * globalScale)
     maxWidth = box.x
 
+  template leftBearing(r: Rune): untyped =
+    textbuffer.font.leftBearing.getOrDefault(r, 0) * globalScale
+
   var
     cursorPos = origin
     lineI = 0
@@ -201,6 +209,10 @@
   of Right:
     cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) / aratio
 
+  # add left bearing for first character at line start
+  if text.len > 0:
+    cursorPos.x = cursorPos.x - leftBearing(text[0])
+
   var bufferOffset = textbuffer.texts[textI].bufferOffset
   let bufferEnd =
     textbuffer.texts[textI].bufferOffset + textbuffer.texts[textI].capacity
@@ -217,6 +229,11 @@
           cursorPos.x = origin.x + ((maxWidth - lineWidths[lineI]) / aratio * 0.5)
         of Right:
           cursorPos.x = origin.x + (maxWidth - lineWidths[lineI]) / aratio
+
+        # add left bearing for first character at line start
+        if text.len > i + 1:
+          cursorPos.x = cursorPos.x - leftBearing(text[i + 1])
+
         cursorPos.y = cursorPos.y - textbuffer.font.lineAdvance * globalScale
       else:
         if not text[i].isWhitespace():
--- a/semicongine/text/font.nim	Wed Jan 01 12:42:43 2025 +0700
+++ b/semicongine/text/font.nim	Wed Jan 01 16:38:27 2025 +0700
@@ -113,7 +113,7 @@
     stbtt_GetCodepointHMetrics(
       addr fi, cint(codePoint), addr advanceUnscaled, addr leftBearingUnscaled
     )
-    var leftBearing = leftBearingUnscaled.float32 * glyph2QuadScale
+    result.leftBearing[codePoint] = leftBearingUnscaled.float32 * glyph2QuadScale
     result.advance[codePoint] = advanceUnscaled.float32 * glyph2QuadScale
 
     let
@@ -123,7 +123,8 @@
       bitmapW = float32(bitmaps[i].width)
       bitmapH = float32(bitmaps[i].height)
       # divide by lineHeightPixels to get from pixel-space to quad-geometry-space
-      left = leftBearing + offsetX[codePoint].float32 / lineHeightPixels
+      left =
+        result.leftBearing[codePoint] + offsetX[codePoint].float32 / lineHeightPixels
       right = left + bitmapW / lineHeightPixels
       top = -offsetY[codePoint].float32 / lineHeightPixels
       bottom = top - bitmapH / lineHeightPixels