diff options
author | bptato <nincsnevem662@gmail.com> | 2024-07-28 00:03:10 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-07-28 00:03:10 +0200 |
commit | 54ed875becbbfaa1a1e86c78592e80f933ebb828 (patch) | |
tree | 855d1cfb1bcdaf9fa913ffda865f0f094ce377c2 | |
parent | bbcf2540627bf3adfd015e71895640e6b09fa379 (diff) | |
download | chawan-54ed875becbbfaa1a1e86c78592e80f933ebb828.tar.gz |
layout: merge neighboring words
Great performance win on large documents.
-rw-r--r-- | src/layout/box.nim | 4 | ||||
-rw-r--r-- | src/layout/engine.nim | 36 | ||||
-rw-r--r-- | src/layout/renderdocument.nim | 20 |
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 |