about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-08-06 20:17:11 +0200
committerbptato <nincsnevem662@gmail.com>2021-08-06 23:23:46 +0200
commit559d2ca622e15c5781303940bc0c87e9aed1e5d1 (patch)
treed16f48ff0187fcf485c445dad298ef0d39e9cb6d
parentda1f249117ad2a6f02c1ac32208d6ece95508505 (diff)
downloadchawan-559d2ca622e15c5781303940bc0c87e9aed1e5d1.tar.gz
More efficient display algorithms
displayBuffer now only kills lines after a replacement has been
outputted. An expermiental procedure displayBufferSwapOutput was also
added, it isn't currently used as it's best for small changes like link
highlighting.
-rw-r--r--src/io/buffer.nim164
-rw-r--r--src/types/color.nim11
-rw-r--r--src/types/enums.nim3
3 files changed, 169 insertions, 9 deletions
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index a9e43868..de600dde 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -6,6 +6,7 @@ import strutils
 import unicode
 
 import ../types/color
+import ../types/enums
 
 import ../utils/twtstr
 import ../utils/eprint
@@ -35,6 +36,22 @@ type
 
   DisplayRow = seq[DisplayCell]
 
+  DrawInstruction = object
+    case t: DrawInstructionType
+    of DRAW_TEXT:
+      text: seq[Rune]
+    of DRAW_GOTO:
+      x: int
+      y: int
+    of DRAW_FGCOLOR, DRAW_BGCOLOR:
+      color: CellColor
+    of DRAW_STYLE:
+      bold: bool
+      italic: bool
+      underline: bool
+    of DRAW_RESET:
+      discard
+
   Buffer* = ref BufferObj
   BufferObj = object
     title*: string
@@ -68,22 +85,104 @@ func newBuffer*(attrs: TermAttributes): Buffer =
   result.attrs = attrs
 
   result.display = newSeq[DisplayCell](result.width * result.height)
+  result.prevdisplay = newSeq[DisplayCell](result.width * result.height)
   result.statusmsg = newSeq[DisplayCell](result.width)
 
-func generateFullOutput*(buffer: Buffer): string =
+func generateFullOutput*(buffer: Buffer): seq[string] =
   var x = 0
   var y = 0
+  var s = ""
   for cell in buffer.display:
     if x >= buffer.width:
       inc y
-      result &= '\n'
+      result.add(s)
       x = 0
+      s = ""
+    s &= $cell.runes
+    inc x
 
-    for r in cell.runes:
-      if r != Rune(0):
-        result &= $r
+  result.add(s)
+
+# generate a sequence of instructions to replace the previous frame with the
+# current one. ideally we should have some mechanism in place to determine
+# where we should use this and where we should just rewrite the frame
+func generateSwapOutput*(buffer: Buffer): seq[DrawInstruction] =
+  var fgcolor: CellColor
+  var bgcolor: CellColor
+  var italic = false
+  var bold = false
+  var underline = false
+
+  let max = buffer.width * buffer.height
+  let curr = buffer.display
+  let prev = buffer.prevdisplay
+  var x = 0
+  var y = 0
+  var cx = 0
+  var cy = 0
+  var i = 0
+  var text: seq[Rune]
+  while i < max:
+    if x >= buffer.width:
+      x = 0
+      cx = 0
+      text &= Rune('\n')
+      inc y
+      inc cy
+
+    if curr[i] != prev[i]:
+      let currwidth = curr[i].runes.width()
+      let prevwidth = prev[i].runes.width()
+      if (curr[i].runes.len > 0 or currwidth < prevwidth) and (x != cx or y != cy):
+        if text.len > 0:
+          result.add(DrawInstruction(t: DRAW_TEXT, text: text))
+          text.setLen(0)
+        result.add(DrawInstruction(t: DRAW_GOTO, x: x, y: y))
+        cx = x
+        cy = y
+
+      let cancont =
+        (curr[i].fgcolor == fgcolor and curr[i].bgcolor == bgcolor and
+         curr[i].italic == italic and curr[i].bold == bold and curr[i].underline == underline)
+
+      if text.len > 0 and not cancont:
+        result.add(DrawInstruction(t: DRAW_TEXT, text: text))
+        text.setLen(0)
+
+      if curr[i].fgcolor != fgcolor:
+        fgcolor = curr[i].fgcolor
+        result.add(DrawInstruction(t: DRAW_FGCOLOR, color: fgcolor))
+
+      if curr[i].bgcolor != bgcolor:
+        bgcolor = curr[i].bgcolor
+        result.add(DrawInstruction(t: DRAW_BGCOLOR, color: bgcolor))
+
+      if curr[i].italic != italic or curr[i].bold != bold or curr[i].underline != underline:
+        if italic and not curr[i].italic or bold and not curr[i].bold or underline and not curr[i].underline:
+          result.add(DrawInstruction(t: DRAW_RESET))
+          if fgcolor != defaultColor:
+            result.add(DrawInstruction(t: DRAW_FGCOLOR, color: fgcolor))
+          if bgcolor != defaultColor:
+            result.add(DrawInstruction(t: DRAW_BGCOLOR, color: bgcolor))
+        italic = curr[i].italic
+        bold = curr[i].bold
+        underline = curr[i].underline
+        result.add(DrawInstruction(t: DRAW_STYLE, italic: italic, bold: bold, underline: underline))
+
+      text &= curr[i].runes
+      if currwidth < prevwidth:
+        var j = 0
+        while j < prevwidth - currwidth:
+          text &= Rune(' ')
+          inc j
+      if text.len > 0:
+        inc cx
 
     inc x
+    inc i
+  
+  if text.len > 0:
+    result.add(DrawInstruction(t: DRAW_TEXT, text: text))
 
 func generateStatusMessage*(buffer: Buffer): string =
   for cell in buffer.statusmsg:
@@ -521,7 +620,6 @@ proc renderPlainText*(buffer: Buffer, text: string) =
   if line.len > 0:
     buffer.setText(0, y, line.toRunes())
 
-  buffer.refreshDisplay()
 
 proc cursorBufferPos(buffer: Buffer) =
   let x = max(buffer.cursorx - buffer.fromx, 0)
@@ -553,10 +651,58 @@ proc statusMsgForBuffer(buffer: Buffer) =
     msg &= " " & buffer.hovertext
   buffer.setStatusMessage(msg)
 
+proc displayBufferSwapOutput(buffer: Buffer) =
+  termGoto(0, 0)
+  let instructions = buffer.generateSwapOutput()
+  for inst in instructions:
+    case inst.t
+    of DRAW_TEXT:
+      print(inst.text)
+    of DRAW_GOTO:
+      termGoto(inst.x, inst.y)
+    of DRAW_FGCOLOR:
+      let color = inst.color
+      if inst.color.rgb:
+        let rgb = color.rgbcolor
+        print("\e38;2" & $rgb.r & ";" & $rgb.b & ";" & $rgb.g)
+      else:
+        print("\e[" & $color.color & "m")
+    of DRAW_BGCOLOR:
+      let color = inst.color
+      if inst.color.rgb:
+        let rgb = color.rgbcolor
+        print("\e48;2" & $rgb.r & ";" & $rgb.b & ";" & $rgb.g)
+      else:
+        print("\e[" & $color.color & "m")
+    of DRAW_STYLE:
+      var os = "\e["
+      var p = false
+      if inst.italic:
+        os &= "3"
+        p = true
+      if inst.bold:
+        if p:
+          os &= ";"
+        os &= "1"
+        p = true
+      if inst.underline:
+        if p:
+          os &= ";"
+        os &= "4"
+        p = true
+      os &= "m"
+      print(os)
+    of DRAW_RESET:
+      print("\e[0m")
+
 proc displayBuffer(buffer: Buffer) =
-  eraseScreen()
   termGoto(0, 0)
-  print(buffer.generateFullOutput().ansiReset())
+  let full = buffer.generateFullOutput()
+  for line in full:
+    print(line)
+    #TODO
+    print("\e[K")
+    print('\n')
 
 proc displayStatusMessage(buffer: Buffer) =
   termGoto(0, buffer.height)
@@ -643,6 +789,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
 
     if reshape:
       buffer.reshape()
+      buffer.displayBuffer()
     if redraw:
       buffer.refreshDisplay()
       buffer.displayBuffer()
@@ -654,6 +801,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
 
 proc displayPage*(attrs: TermAttributes, buffer: Buffer): bool =
   discard buffer.gotoAnchor()
+  buffer.refreshDisplay()
   buffer.displayBuffer()
   buffer.statusMsgForBuffer()
   return inputLoop(attrs, buffer)
diff --git a/src/types/color.nim b/src/types/color.nim
index 2a956baa..45a3f31d 100644
--- a/src/types/color.nim
+++ b/src/types/color.nim
@@ -4,6 +4,15 @@ type
   CellColor* = object
     case rgb*: bool
     of true:
-      rgbcolor: RGBColor
+      rgbcolor*: RGBColor
     of false:
       color*: uint8
+
+func `==`*(color1: CellColor, color2: CellColor): bool =
+  if color1.rgb != color2.rgb:
+    return false
+  if color1.rgb:
+    return color1.rgbcolor == color2.rgbcolor
+  return color1.color == color2.color
+
+const defaultColor* = CellColor(rgb: false, color: 0)
diff --git a/src/types/enums.nim b/src/types/enums.nim
index 6b2c7412..eac71dd5 100644
--- a/src/types/enums.nim
+++ b/src/types/enums.nim
@@ -78,6 +78,9 @@ type
   CSSGlobalValueType* = enum
     VALUE_INITIAL, VALUE_INHERIT, VALUE_REVERT, VALUE_UNSET
 
+  DrawInstructionType* = enum
+    DRAW_TEXT, DRAW_GOTO, DRAW_FGCOLOR, DRAW_BGCOLOR, DRAW_STYLE, DRAW_RESET
+
 const SelfClosingTagTypes* = {
   TAG_LI, TAG_P
 }