about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-08-28 19:32:03 +0200
committerbptato <nincsnevem662@gmail.com>2024-08-28 19:32:03 +0200
commit8c8c19f7cb49ff5cb73ea12317e0ff54ecb6c53a (patch)
treea330d30f72fc0e6695b1034e12170108e419a619
parenta4d44bb0d60e18b4c7a44e89d4493734cefea297 (diff)
downloadchawan-8c8c19f7cb49ff5cb73ea12317e0ff54ecb6c53a.tar.gz
sixel, term: fix off-by-1's in cropping
& clean up outputSixelImage in general
-rw-r--r--adapter/img/sixel.nim8
-rw-r--r--src/local/term.nim86
2 files changed, 42 insertions, 52 deletions
diff --git a/adapter/img/sixel.nim b/adapter/img/sixel.nim
index 7f4514d9..e06b85e7 100644
--- a/adapter/img/sixel.nim
+++ b/adapter/img/sixel.nim
@@ -302,10 +302,13 @@ proc encode(s: string; width, height, offx, offy, cropw: int; halfdump: bool;
         bands[k].data[j] = bands[k].data[j] or mask
       n += W
     outs.setLen(0)
-    for i in 0 ..< bands.high:
+    var i = 0
+    while true:
       outs &= bands[i].compressSixel()
+      inc i
+      if i >= bands.len:
+        break
       outs &= '$'
-    outs &= bands[^1].compressSixel()
     if n >= H:
       outs &= ST
     else:
@@ -314,6 +317,7 @@ proc encode(s: string; width, height, offx, offy, cropw: int; halfdump: bool;
     stdout.write(outs)
   if halfdump:
     ymap.putU32BE(uint32(totalLen))
+    ymap.putU32BE(uint32(ymap.len))
     stdout.write(ymap)
 
 proc parseDimensions(s: string): (int, int) =
diff --git a/src/local/term.nim b/src/local/term.nim
index f2faaa13..afeb12df 100644
--- a/src/local/term.nim
+++ b/src/local/term.nim
@@ -739,12 +739,11 @@ proc loadImage*(term: Terminal; bmp: Bitmap; data: Blob; pid, imageId,
   # no longer on screen
   return nil
 
-func getOffYIdx(data: openArray[char]; y, starti: int): int32 =
-  let i = starti + (y div 6) * 4
-  return int32(data[i]) or
-    (int32(data[i + 1]) shl 8) or
-    (int32(data[i + 2]) shl 16) or
-    (int32(data[i + 3]) shl 24)
+func getU32BE(data: openArray[char]; i: int): uint32 =
+  return uint32(data[i]) or
+    (uint32(data[i + 1]) shl 8) or
+    (uint32(data[i + 2]) shl 16) or
+    (uint32(data[i + 3]) shl 24)
 
 proc outputSixelImage(term: Terminal; x, y: int; image: CanvasImage;
     data: openArray[char]) =
@@ -757,56 +756,43 @@ proc outputSixelImage(term: Terminal; x, y: int; image: CanvasImage;
   outs &= DCSSTART & 'q'
   # set raster attributes
   let realw = dispw - offx
-  var realh = disph - offy
-  #if disph < int(bmp.height):
-  #  realh -= image.erry
+  let realh = disph - offy
   outs &= "\"1;1;" & $realw & ';' & $realh
   term.write(outs)
-  let sraLen = uint32(data[0]) or
-    (uint32(data[1]) shl 8) or
-    (uint32(data[2]) shl 16) or
-    (uint32(data[3]) shl 24)
-  let preludeLen = int(sraLen + 4)
-  term.write(data.toOpenArray(4, 4 + int(sraLen) - 1))
-  let lookupTableLen = ((int(bmp.height) + 5) div 6 + 1) * 4
-  let L = data.len - lookupTableLen
+  let sraLen = int(data.getU32BE(0))
+  let preludeLen = sraLen + 4
+  term.write(data.toOpenArray(4, 4 + sraLen - 1))
+  let lookupTableLen = int(data.getU32BE(data.len - 4))
+  let L = data.len - lookupTableLen - 4
   # Note: we only crop images when it is possible to do so in near constant
   # time. Otherwise, the image is re-coded in a cropped form.
-  if realh == int(bmp.height):
+  if realh == int(bmp.height): # don't crop
     term.write(data.toOpenArray(preludeLen, L - 1))
   else:
-    let offyi = data.getOffYIdx(offy, L)
-    var e = disph
-    if disph < int(bmp.height):
-      e -= image.erry
-    let endyi = data.getOffYIdx(e, L)
-    if endyi <= offyi:
-      return
-    let si = preludeLen + int(offyi)
-    let ei = preludeLen + int(endyi) - 1
-    assert offyi < endyi
-    assert ei <= data.len - lookupTableLen
-    term.write(data.toOpenArray(si, ei - 1))
-    var ndash = 0
-    for c in data.toOpenArray(si, ei - 1):
-      if c == '-':
-        inc ndash
-    let herry = realh - (realh div 6) * 6
-    if herry > 0 and disph < int(bmp.height):
-      # can't write out the last row completely; mask off the bottom part.
-      let mask = (1u8 shl herry) - 1
-      var s = "-"
-      var i = ei + 1
-      inc ndash
-      while i < L and (let c = data[i]; c notin {'-', '\e'}): # newline or ST
-        let u = uint8(c) - 0x3F # may underflow, but that's no problem
-        if u < 0x40:
-          s &= char((u and mask) + 0x3F)
-        else:
-          s &= c
-        inc i
-      term.write(s)
-    term.write(ST)
+    let si = preludeLen + int(data.getU32BE(L + (offy div 6) * 4))
+    if disph == int(bmp.height): # crop top only
+      term.write(data.toOpenArray(si, L - 1))
+    else: # crop both top & bottom
+      let ed6 = (disph - image.erry) div 6
+      let ei = preludeLen + int(data.getU32BE(L + ed6 * 4)) - 1
+      term.write(data.toOpenArray(si, ei - 1))
+      # calculate difference between target Y & actual position in the map
+      # note: it must be offset by image.erry; that's where the map starts.
+      let herry = disph - (ed6 * 6 + image.erry)
+      if herry > 0:
+        # can't write out the last row completely; mask off the bottom part.
+        let mask = (1u8 shl herry) - 1
+        var s = "-"
+        var i = ei + 1
+        while i < L and (let c = data[i]; c notin {'-', '\e'}): # newline or ST
+          let u = uint8(c) - 0x3F # may underflow, but that's no problem
+          if u < 0x40:
+            s &= char((u and mask) + 0x3F)
+          else:
+            s &= c
+          inc i
+        term.write(s)
+      term.write(ST)
 
 proc outputSixelImage(term: Terminal; x, y: int; image: CanvasImage) =
   var p = cast[ptr UncheckedArray[char]](image.data.buffer)