about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-03-26 00:32:04 +0100
committerbptato <nincsnevem662@gmail.com>2024-03-26 01:16:58 +0100
commitce4d5259a9d6377654e4db1c39dfbc8db0fab2fe (patch)
tree5e1a776c165c90395f64bae8c000e944e8885daf
parentefd615e76f5a1c0f19951b69f793885cb2cb75d1 (diff)
downloadchawan-ce4d5259a9d6377654e4db1c39dfbc8db0fab2fe.tar.gz
layout: optimize inline background areas
Instead of adding a new area for every single line, extend already
existing areas when possible.

(This was my original plan but I got lazy.)
-rw-r--r--src/layout/engine.nim69
1 files changed, 51 insertions, 18 deletions
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index 52b551b1..1459b65d 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -544,15 +544,13 @@ proc addBackgroundAreas(ictx: var InlineContext; rootFragment: InlineFragment) =
   var currentStack: seq[InlineFragment] = @[]
   template top: InlineFragment = currentStack[^1]
   var atomIdx = 0
-  for i, line in ictx.lines:
+  var lineSkipped = false
+  for line in ictx.lines:
     if line.atoms.len == 0:
+      # no atoms here; set lineSkipped to true so that we don't accidentally
+      # extend background areas over this
+      lineSkipped = true
       continue
-    # extend current areas
-    for node in currentStack:
-      node.areas.add(Area(
-        offset: Offset(x: line.atoms[0].offset.x, y: line.offsety),
-        size: Size(w: line.atoms[0].size.w, h: line.height)
-      ))
     var prevEnd: LayoutUnit = 0
     for atom in line.atoms:
       if currentStack.len == 0 or atomIdx >= top.atoms.len:
@@ -562,14 +560,32 @@ proc addBackgroundAreas(ictx: var InlineContext; rootFragment: InlineFragment) =
           if thisNode == nil: # sentinel found
             let oldTop = currentStack.pop()
             # finish oldTop area
-            # (must have at least one area on this line b/c we add it at start)
-            assert oldTop.areas[^1].offset.y == line.offsety
-            if prevEnd > 0:
-              oldTop.areas[^1].size.w = prevEnd - oldTop.areas[^1].offset.x
-            else:
-              # fragment got dropped without prevEnd moving anywhere; delete
-              # area
-              oldTop.areas.setLen(oldTop.areas.high)
+            if oldTop.areas[^1].offset.y == line.offsety:
+              # if offset.y is this offsety, then it means that we added it on
+              # this line, so we just have to set its width
+              if prevEnd > 0:
+                oldTop.areas[^1].size.w = prevEnd - oldTop.areas[^1].offset.x
+              else:
+                # fragment got dropped without prevEnd moving anywhere; delete
+                # area
+                oldTop.areas.setLen(oldTop.areas.high)
+            elif prevEnd > 0:
+              # offset.y is presumably from a previous line
+              # (if prevEnd is 0, then the area doesn't extend to this line,
+              # so we do not have to do anything.)
+              let x = line.atoms[0].offset.x
+              let w = prevEnd - x
+              if oldTop.areas[^1].offset.x == x and
+                  oldTop.areas[^1].size.w == w:
+                # same vertical dimensions; just extend.
+                oldTop.areas[^1].size.h = line.offsety + line.height -
+                  oldTop.areas[^1].offset.y
+              else:
+                # vertical dimensions differ; add new area.
+                oldTop.areas.add(Area(
+                  offset: Offset(x: x, y: line.offsety),
+                  size: Size(w: w, h: line.height)
+                ))
             continue
           traverseStack.add(nil) # sentinel
           for i in countdown(thisNode.children.high, 0):
@@ -584,10 +600,27 @@ proc addBackgroundAreas(ictx: var InlineContext; rootFragment: InlineFragment) =
       prevEnd = atom.offset.x + atom.size.w
       assert top.atoms[atomIdx] == atom
       inc atomIdx
-    # finish areas of nodes currently on the stack
+    # extend current areas
     for node in currentStack:
-      assert node.areas[^1].offset.y == line.offsety
-      node.areas[^1].size.w = prevEnd - node.areas[^1].offset.x
+      if node.areas[^1].offset.y == line.offsety:
+        # added in this iteration. no need to extend vertically, but make sure
+        # that it reaches prevEnd.
+        node.areas[^1].size.w = prevEnd - node.areas[^1].offset.x
+        continue
+      let x1 = node.areas[^1].offset.x
+      let x2 = node.areas[^1].offset.x + node.areas[^1].size.w
+      if x1 == line.atoms[0].offset.x and x2 == prevEnd and not lineSkipped:
+        # horizontal dimensions are the same as for the last area. just move its
+        # vertical end to the current line's end.
+        node.areas[^1].size.h = line.offsety + line.height -
+          node.areas[^1].offset.y
+      else:
+        # horizontal dimensions differ; add a new area
+        node.areas.add(Area(
+          offset: Offset(x: line.atoms[0].offset.x, y: line.offsety),
+          size: Size(w: prevEnd - line.atoms[0].offset.x, h: line.height)
+        ))
+    lineSkipped = false
 
 func minwidth(atom: InlineAtom): LayoutUnit =
   if atom.t == iatInlineBlock: