about summary refs log tree commit diff stats
path: root/src/local
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-06-29 12:32:17 +0200
committerbptato <nincsnevem662@gmail.com>2024-06-29 12:50:16 +0200
commit2e50aa23237da76802d2a61cb7426bf51c122d14 (patch)
tree9ed3f42f9ef3f6a6f97c4118bc5fbc90248f00ce /src/local
parente7786e39e38ddf5ec75b95cc19e1bee108cd37d2 (diff)
downloadchawan-2e50aa23237da76802d2a61cb7426bf51c122d14.tar.gz
dom, pager: cache images from network
With many limitations:

* slightly randomized expiry, so it's harder to fingerprint
* only images. so e.g. CSS is still left uncached
* it's per-buffer and non-persistent, so images are still redownloaded
  for every new page load

so it's more of an image sharing between placements than true caching.
Diffstat (limited to 'src/local')
-rw-r--r--src/local/container.nim12
-rw-r--r--src/local/pager.nim37
-rw-r--r--src/local/term.nim28
3 files changed, 44 insertions, 33 deletions
diff --git a/src/local/container.nim b/src/local/container.nim
index a7702101..d78f9120 100644
--- a/src/local/container.nim
+++ b/src/local/container.nim
@@ -99,6 +99,8 @@ type
 
   CachedImage* = ref object
     loaded*: bool
+    width*: int
+    height*: int
     bmp*: NetworkBitmap
 
   Container* = ref object
@@ -2038,10 +2040,12 @@ proc highlightMarks*(container: Container; display: var FixedGrid;
       hlformat.bgcolor = hlcolor
       display[y * display.width + x].format = hlformat
 
-func findCachedImage*(container: Container; id: int): CachedImage =
-  for image in container.cachedImages:
-    if image.bmp.imageId == id:
-      return image
+func findCachedImage*(container: Container; image: PosBitmap): CachedImage =
+  let imageId = NetworkBitmap(image.bmp).imageId
+  for it in container.cachedImages:
+    if it.bmp.imageId == imageId and it.width == image.width and
+        it.height == image.height:
+      return it
   return nil
 
 proc handleEvent*(container: Container) =
diff --git a/src/local/pager.nim b/src/local/pager.nim
index cdc08422..9f5eb593 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -474,17 +474,14 @@ proc redraw(pager: Pager) {.jsfunc.} =
       pager.container.select.redraw = true
 
 proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) =
-  #TODO this is kinda dumb, because we cannot unload cached images.
-  # ideally the filesystem cache should serve as the only cache, but right
-  # now it's just sort of a temporary place before the image is dumped to
-  # memory.
-  # maybe allow the buffer to add a cache file? or receive a separate "image
-  # load start" event in container, and then add one in the pager?
-  # the first option seems better; it's simpler, and buffers can add arbitrary
-  # cache files if they just tell the pager it's an image anyway.
+  #TODO we should only cache the final output in memory, not the full bitmap.
   let bmp = NetworkBitmap(image.bmp)
   let request = newRequest(newURL("cache:" & $bmp.cacheId).get)
-  let cachedImage = CachedImage(bmp: bmp)
+  let cachedImage = CachedImage(
+    bmp: bmp,
+    width: image.width,
+    height: image.height
+  )
   pager.loader.shareCachedItem(bmp.cacheId, pager.loader.clientPid,
     container.process)
   pager.loader.fetch(request).then(proc(res: JSResult[Response]):
@@ -508,15 +505,18 @@ proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) =
     response.body.sclose()
     return r
   ).then(proc(res: JSResult[Response]): EmptyPromise =
+    if res.isNone:
+      pager.loader.removeCachedItem(bmp.cacheId)
+      return newResolvedPromise()
     let response = res.get
     # take target sizes
     bmp.width = uint64(image.width)
     bmp.height = uint64(image.height)
-    return response.saveToBitmap(bmp)
-  ).then(proc() =
-    container.redraw = true
-    cachedImage.loaded = true
-    pager.loader.removeCachedItem(bmp.cacheId)
+    return response.saveToBitmap(bmp).then(proc() =
+      container.redraw = true
+      cachedImage.loaded = true
+      pager.loader.removeCachedItem(bmp.cacheId)
+    )
   )
   container.cachedImages.add(cachedImage)
 
@@ -525,9 +525,8 @@ proc initImages(pager: Pager; container: Container) =
   for image in container.images:
     var imageId = -1
     if image.bmp of NetworkBitmap:
-      # add cache file to pager, but source it from the container.
       let bmp = NetworkBitmap(image.bmp)
-      let cached = container.findCachedImage(bmp.imageId)
+      let cached = container.findCachedImage(image)
       imageId = bmp.imageId
       if cached == nil:
         pager.loadCachedImage(container, image)
@@ -538,7 +537,7 @@ proc initImages(pager: Pager; container: Container) =
     else:
       imageId = pager.imageId
       inc pager.imageId
-    let canvasImage = pager.term.loadImage(image.bmp, container.process, imageId,
+    let canvasImage = pager.term.loadImage(image, container.process, imageId,
       image.x - container.fromx, image.y - container.fromy, pager.bufWidth,
       pager.bufHeight)
     if canvasImage != nil:
@@ -548,6 +547,7 @@ proc initImages(pager: Pager; container: Container) =
 
 proc draw*(pager: Pager) =
   var redraw = false
+  var imageRedraw = false
   let container = pager.container
   if container != nil:
     if container.redraw:
@@ -558,6 +558,7 @@ proc draw*(pager: Pager) =
         container.highlightMarks(pager.display.grid, hlcolor)
       container.redraw = false
       pager.display.redraw = true
+      imageRedraw = true
     if (let select = container.select; select != nil and select.redraw):
       select.drawSelect(pager.display.grid)
       select.redraw = false
@@ -580,7 +581,7 @@ proc draw*(pager: Pager) =
     pager.term.writeGrid(pager.status.grid, 0, pager.attrs.height - 1)
     pager.status.redraw = false
     redraw = true
-  if container != nil and pager.term.imageMode != imNone:
+  if imageRedraw and pager.term.imageMode != imNone:
     # init images only after term canvas has been finalized
     pager.initImages(container)
   if redraw:
diff --git a/src/local/term.nim b/src/local/term.nim
index 546b16fa..8dcff9ba 100644
--- a/src/local/term.nim
+++ b/src/local/term.nim
@@ -7,10 +7,14 @@ import std/termios
 import std/unicode
 
 import bindings/termcap
+import chagashi/charset
+import chagashi/decoder
+import chagashi/encoder
 import config/config
 import img/bitmap
 import io/posixstream
 import js/base64
+import layout/renderdocument
 import types/cell
 import types/color
 import types/opt
@@ -18,10 +22,6 @@ import types/winattrs
 import utils/strwidth
 import utils/twtstr
 
-import chagashi/charset
-import chagashi/decoder
-import chagashi/encoder
-
 #TODO switch away from termcap...
 
 type
@@ -66,7 +66,7 @@ type
     damaged: bool
     marked*: bool
     kittyId: int
-    bmp: Bitmap
+    pbmp: PosBitmap
 
   Terminal* = ref object
     cs*: Charset
@@ -222,6 +222,9 @@ const ANSIColorMap = [
   rgb(255, 255, 255)
 ]
 
+template bmp(image: CanvasImage): Bitmap =
+  image.pbmp.bmp
+
 proc flush*(term: Terminal) =
   term.outfile.flushFile()
 
@@ -616,9 +619,12 @@ proc outputGrid*(term: Terminal) =
   term.cursorx = -1
   term.cursory = -1
 
-func findImage(term: Terminal; pid, imageId: int): CanvasImage =
+func findImage(term: Terminal; pid, imageId: int; pbmp: PosBitmap):
+    CanvasImage =
   for it in term.canvasImages:
-    if it.pid == pid and it.imageId == imageId:
+    if it.pid == pid and it.imageId == imageId and
+        it.pbmp.width == pbmp.width and it.pbmp.height == pbmp.height and
+        it.pbmp.x == pbmp.x and it.pbmp.y == pbmp.y:
       return it
   return nil
 
@@ -660,9 +666,9 @@ proc clearImages*(term: Terminal; maxh: int) =
       term.clearImage(image, maxh)
     image.marked = false
 
-proc loadImage*(term: Terminal; bmp: Bitmap; pid, imageId, x, y, maxw,
+proc loadImage*(term: Terminal; pbmp: PosBitmap; pid, imageId, x, y, maxw,
     maxh: int): CanvasImage =
-  if (let image = term.findImage(pid, imageId); image != nil):
+  if (let image = term.findImage(pid, imageId, pbmp); image != nil):
     # reuse image on screen
     if image.x != x or image.y != y:
       # only clear sixels; with kitty we just move the existing image
@@ -672,7 +678,7 @@ proc loadImage*(term: Terminal; bmp: Bitmap; pid, imageId, x, y, maxw,
         # no longer on screen
         return nil
     elif term.imageMode == imSixel:
-      # check if any line our image is on is damaged
+      # check if any line of our image is damaged
       let ey = min(image.y + int(image.bmp.height), maxh)
       let mx = (image.offx + image.dispw) div term.attrs.ppc
       for y in max(image.y, 0) ..< ey:
@@ -684,7 +690,7 @@ proc loadImage*(term: Terminal; bmp: Bitmap; pid, imageId, x, y, maxw,
     image.marked = true
     return image
   # new image
-  let image = CanvasImage(bmp: bmp, pid: pid, imageId: imageId)
+  let image = CanvasImage(pbmp: pbmp, pid: pid, imageId: imageId)
   if term.positionImage(image, x, y, maxw, maxh):
     return image
   # no longer on screen