about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-07-28 00:03:10 +0200
committerbptato <nincsnevem662@gmail.com>2024-07-28 00:03:10 +0200
commit54ed875becbbfaa1a1e86c78592e80f933ebb828 (patch)
tree855d1cfb1bcdaf9fa913ffda865f0f094ce377c2
parentbbcf2540627bf3adfd015e71895640e6b09fa379 (diff)
downloadchawan-54ed875becbbfaa1a1e86c78592e80f933ebb828.tar.gz
layout: merge neighboring words
Great performance win on large documents.
-rw-r--r--src/layout/box.nim4
-rw-r--r--src/layout/engine.nim36
-rw-r--r--src/layout/renderdocument.nim20
3 files changed, 28 insertions, 32 deletions
diff --git a/src/layout/box.nim b/src/layout/box.nim
index e4005a54..9812a9cd 100644
--- a/src/layout/box.nim
+++ b/src/layout/box.nim
@@ -14,14 +14,12 @@ type
   Overflow* = array[DimensionType, Span]
 
   InlineAtomType* = enum
-    iatSpacing, iatWord, iatInlineBlock, iatImage
+    iatWord, iatInlineBlock, iatImage
 
   InlineAtom* = ref object
     offset*: Offset
     size*: Size
     case t*: InlineAtomType
-    of iatSpacing:
-      discard
     of iatWord:
       str*: string
     of iatInlineBlock:
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index 18472b16..b92676dc 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -304,7 +304,10 @@ func computeShift(ictx: InlineContext; state: InlineState): LayoutUnit =
     # skip line feed between double-width characters
     return 0
   if not state.fragment.computed.whitespacepre:
-    if ictx.lbstate.atoms.len == 0 or ictx.lbstate.atoms[^1].t == iatSpacing:
+    if ictx.lbstate.atoms.len == 0:
+      return 0
+    let atom = ictx.lbstate.atoms[^1]
+    if atom.t == iatWord and atom.str[^1] == ' ':
       return 0
   return ictx.cellWidth * ictx.whitespacenum
 
@@ -527,17 +530,24 @@ proc putAtom(state: var LineBoxState; atom: InlineAtom;
 
 proc addSpacing(ictx: var InlineContext; width, height: LayoutUnit;
     state: InlineState; hang = false) =
-  let spacing = InlineAtom(
-    t: iatSpacing,
-    size: size(w = width, h = height),
-    offset: offset(x = ictx.lbstate.size.w, y = height)
-  )
-  let iastate = InlineAtomState(baseline: height)
+  let fragment = ictx.whitespaceFragment
+  if fragment.state.atoms.len == 0 or fragment.state.atoms[^1].t != iatWord:
+    let atom = InlineAtom(
+      t: iatWord,
+      size: size(w = 0, h = height),
+      offset: offset(x = ictx.lbstate.size.w, y = height)
+    )
+    let iastate = InlineAtomState(baseline: height)
+    ictx.lbstate.putAtom(atom, iastate, fragment)
+  let atom = fragment.state.atoms[^1]
+  let n = (width div ictx.cellWidth).toInt #TODO
+  for i in 0 ..< n:
+    atom.str &= ' '
+  atom.size.w += width
   if not hang:
     # In some cases, whitespace may "hang" at the end of the line. This means
     # it is written, but is not actually counted in the box's width.
     ictx.lbstate.size.w += width
-  ictx.lbstate.putAtom(spacing, iastate, ictx.whitespaceFragment)
 
 proc flushWhitespace(ictx: var InlineContext; state: InlineState;
     hang = false) =
@@ -662,7 +672,15 @@ proc addAtom(ictx: var InlineContext; state: var InlineState;
       ictx.addSpacing(shift, ictx.cellHeight, state)
     ictx.root.state.xminwidth = max(ictx.root.state.xminwidth, atom.xminwidth)
     ictx.applyLineHeight(ictx.lbstate, state.fragment.computed)
-    if atom.t != iatWord:
+    if atom.t == iatWord:
+      if ictx.lbstate.atoms.len > 0 and state.fragment.state.atoms.len > 0:
+        let oatom = ictx.lbstate.atoms[^1]
+        if oatom.t == iatWord and oatom == state.fragment.state.atoms[^1]:
+          oatom.str &= atom.str
+          oatom.size.w += atom.size.w
+          ictx.lbstate.size.w += atom.size.w
+          return
+    else:
       ictx.lbstate.charwidth = 0
     ictx.lbstate.putAtom(atom, iastate, state.fragment)
     atom.offset.x += ictx.lbstate.size.w
diff --git a/src/layout/renderdocument.nim b/src/layout/renderdocument.nim
index 939e9035..125e53a6 100644
--- a/src/layout/renderdocument.nim
+++ b/src/layout/renderdocument.nim
@@ -261,24 +261,6 @@ proc setRowWord(grid: var FlexibleGrid; state: var RenderState;
   var x = toInt((offset.x + word.offset.x) div state.attrs.ppc) # x cell
   grid.setText(word.str, x, y, format, node)
 
-proc setSpacing(grid: var FlexibleGrid; state: var RenderState;
-    spacing: InlineAtom; offset: Offset; format: Format; node: StyledNode) =
-  let y = toInt((offset.y + spacing.offset.y) div state.attrs.ppl) # y cell
-  if y < 0: return # y is outside the canvas, no need to draw
-  var x = toInt((offset.x + spacing.offset.x) div state.attrs.ppc) # x cell
-  let width = toInt(spacing.size.w div state.attrs.ppc) # cell width
-  if x + width < 0:
-    return # highest x is outside the canvas, no need to draw
-  var i = 0
-  if x < 0:
-    i -= x
-    x = 0
-  if i < width:
-    # make sure we have line y
-    if grid.high < y:
-      grid.addLines(y - grid.high)
-    grid[y].setText(' '.repeat(width - i), x, format, node)
-
 proc paintBackground(grid: var FlexibleGrid; state: var RenderState;
     color: CellColor; startx, starty, endx, endy: int; node: StyledNode) =
   var starty = starty div state.attrs.ppl
@@ -388,8 +370,6 @@ proc renderInlineFragment(grid: var FlexibleGrid; state: var RenderState;
         grid.renderBlockBox(state, atom.innerbox, offset + atom.offset)
       of iatWord:
         grid.setRowWord(state, atom, offset, format, fragment.node)
-      of iatSpacing:
-        grid.setSpacing(state, atom, offset, format, fragment.node)
       of iatImage:
         let x1 = offset.x.toInt
         let y1 = offset.y.toInt