diff options
author | bptato <nincsnevem662@gmail.com> | 2024-07-02 16:56:33 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-07-02 17:50:10 +0200 |
commit | 02b549ee0c7bda8ad110eba6aac14bafb10d45e3 (patch) | |
tree | 4628d8fbba059098f7e6fdc78fd3ab5070cb8820 /src/local | |
parent | c69f9f6c1bc18b718a8c8deb11934cca19929e02 (diff) | |
download | chawan-02b549ee0c7bda8ad110eba6aac14bafb10d45e3.tar.gz |
pager: PNGify kitty images, clear images on buffer switch
Saves bandwidth; it's especially useful over SSH. Still not sure if this is the right solution, since it now needs two select cycles instead of one, and it does yet another copy of the image. (Unnecessarily, because stbi cannot stream its output, and stbiw cannot stream its input.) Also, to save memory, we now discard decoded images of buffers that are not being viewed.
Diffstat (limited to 'src/local')
-rw-r--r-- | src/local/container.nim | 2 | ||||
-rw-r--r-- | src/local/pager.nim | 61 | ||||
-rw-r--r-- | src/local/term.nim | 38 |
3 files changed, 73 insertions, 28 deletions
diff --git a/src/local/container.nim b/src/local/container.nim index cc1e75e5..9f8dbd9e 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -22,6 +22,7 @@ import monoucha/javascript import monoucha/jsregex import monoucha/jstypes import server/buffer +import types/blob import types/cell import types/color import types/cookie @@ -101,6 +102,7 @@ type loaded*: bool width*: int height*: int + data*: Blob bmp*: NetworkBitmap Container* = ref object diff --git a/src/local/pager.nim b/src/local/pager.nim index 9f5eb593..f3d4d79a 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -40,6 +40,7 @@ import monoucha/quickjs import monoucha/tojs import server/buffer import server/forkserver +import types/blob import types/cell import types/color import types/cookie @@ -208,6 +209,8 @@ proc clearStatus(pager: Pager) = ) proc setContainer*(pager: Pager; c: Container) {.jsfunc.} = + if pager.term.imageMode != imNone and pager.container != nil: + pager.container.cachedImages.setLen(0) pager.container = c if c != nil: c.queueDraw() @@ -474,8 +477,8 @@ proc redraw(pager: Pager) {.jsfunc.} = pager.container.select.redraw = true proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) = - #TODO we should only cache the final output in memory, not the full bitmap. - let bmp = NetworkBitmap(image.bmp) + let bmp = NetworkBitmap() + bmp[] = NetworkBitmap(image.bmp)[] let request = newRequest(newURL("cache:" & $bmp.cacheId).get) let cachedImage = CachedImage( bmp: bmp, @@ -484,9 +487,11 @@ proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) = ) pager.loader.shareCachedItem(bmp.cacheId, pager.loader.clientPid, container.process) + let imageMode = pager.term.imageMode pager.loader.fetch(request).then(proc(res: JSResult[Response]): Promise[JSResult[Response]] = if res.isNone: + pager.loader.removeCachedItem(bmp.cacheId) return let response = res.get let headers = newHeaders() @@ -504,19 +509,46 @@ proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) = response.unregisterFun() response.body.sclose() return r - ).then(proc(res: JSResult[Response]): EmptyPromise = + ).then(proc(res: JSResult[Response]) = if res.isNone: pager.loader.removeCachedItem(bmp.cacheId) - return newResolvedPromise() + return 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) - ) + case imageMode + of imSixel: + #TODO we should only cache the final output in memory, not the full + # bitmap. + response.saveToBitmap(bmp).then(proc() = + container.redraw = true + cachedImage.loaded = true + pager.loader.removeCachedItem(bmp.cacheId) + ) + of imKitty: + let headers = newHeaders({ + "Cha-Image-Dimensions": $image.width & 'x' & $image.height + }) + let request = newRequest( + newURL("img-codec+png:encode").get, + httpMethod = hmPost, + headers = headers, + body = RequestBody(t: rbtOutput, outputId: response.outputId), + ) + let r = pager.loader.fetch(request) + response.resume() + response.unregisterFun() + response.body.sclose() + r.then(proc(res: JSResult[Response]): Promise[JSResult[Blob]] = + return res.get.blob() + ).then(proc(res: JSResult[Blob]) = + container.redraw = true + cachedImage.data = res.get + cachedImage.loaded = true + pager.loader.removeCachedItem(bmp.cacheId) + ) + of imNone: assert false ) container.cachedImages.add(cachedImage) @@ -524,6 +556,8 @@ proc initImages(pager: Pager; container: Container) = var newImages: seq[CanvasImage] = @[] for image in container.images: var imageId = -1 + var data: Blob = nil + var bmp0 = image.bmp if image.bmp of NetworkBitmap: let bmp = NetworkBitmap(image.bmp) let cached = container.findCachedImage(image) @@ -531,15 +565,16 @@ proc initImages(pager: Pager; container: Container) = if cached == nil: pager.loadCachedImage(container, image) continue - image.bmp = cached.bmp + bmp0 = cached.bmp + data = cached.data if not cached.loaded: continue # loading else: imageId = pager.imageId inc pager.imageId - let canvasImage = pager.term.loadImage(image, container.process, imageId, - image.x - container.fromx, image.y - container.fromy, pager.bufWidth, - pager.bufHeight) + let canvasImage = pager.term.loadImage(bmp0, data, container.process, + imageId, image.x - container.fromx, image.y - container.fromy, + image.x, image.y, pager.bufWidth, pager.bufHeight) if canvasImage != nil: newImages.add(canvasImage) pager.term.clearImages(pager.bufHeight) diff --git a/src/local/term.nim b/src/local/term.nim index f2b48f36..50eac042 100644 --- a/src/local/term.nim +++ b/src/local/term.nim @@ -14,7 +14,7 @@ import config/config import img/bitmap import io/posixstream import js/base64 -import layout/renderdocument +import types/blob import types/cell import types/color import types/opt @@ -66,7 +66,11 @@ type damaged: bool marked*: bool kittyId: int - pbmp: PosBitmap + bmp: Bitmap + # absolute x, y in container + rx: int + ry: int + data: Blob Terminal* = ref object cs*: Charset @@ -222,9 +226,6 @@ const ANSIColorMap = [ rgb(255, 255, 255) ] -template bmp(image: CanvasImage): Bitmap = - image.pbmp.bmp - proc flush*(term: Terminal) = term.outfile.flushFile() @@ -619,12 +620,12 @@ proc outputGrid*(term: Terminal) = term.cursorx = -1 term.cursory = -1 -func findImage(term: Terminal; pid, imageId: int; pbmp: PosBitmap): +func findImage(term: Terminal; pid, imageId: int; bmp: Bitmap; rx, ry: int): CanvasImage = for it in term.canvasImages: 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: + it.bmp.width == bmp.width and it.bmp.height == bmp.height and + it.rx == rx and it.ry == ry: return it return nil @@ -666,9 +667,9 @@ proc clearImages*(term: Terminal; maxh: int) = term.clearImage(image, maxh) image.marked = false -proc loadImage*(term: Terminal; pbmp: PosBitmap; pid, imageId, x, y, maxw, - maxh: int): CanvasImage = - if (let image = term.findImage(pid, imageId, pbmp); image != nil): +proc loadImage*(term: Terminal; bmp: Bitmap; data: Blob; pid, imageId, + x, y, rx, ry, maxw, maxh: int): CanvasImage = + if (let image = term.findImage(pid, imageId, bmp, rx, ry); 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 @@ -690,7 +691,14 @@ proc loadImage*(term: Terminal; pbmp: PosBitmap; pid, imageId, x, y, maxw, image.marked = true return image # new image - let image = CanvasImage(pbmp: pbmp, pid: pid, imageId: imageId) + let image = CanvasImage( + bmp: bmp, + pid: pid, + imageId: imageId, + data: data, + rx: rx, + ry: ry + ) if term.positionImage(image, x, y, maxw, maxh): return image # no longer on screen @@ -802,10 +810,10 @@ proc outputKittyImage(term: Terminal; x, y: int; image: CanvasImage) = const MaxBytes = 4096 * 3 div 4 var i = MaxBytes # transcode to RGB - let p = cast[ptr UncheckedArray[uint8]](addr image.bmp.px[0]) - let L = image.bmp.px.len * 4 + let p = cast[ptr UncheckedArray[uint8]](image.data.buffer) + let L = int(image.data.size) let m = if i < L: '1' else: '0' - outs &= ",a=T,f=32,m=" & m & ';' + outs &= ",a=T,f=100,m=" & m & ';' outs.btoa(p.toOpenArray(0, min(L, i) - 1)) outs &= ST term.write(outs) |