diff options
-rw-r--r-- | adapter/img/stbi.nim | 12 | ||||
-rw-r--r-- | src/local/container.nim | 2 | ||||
-rw-r--r-- | src/local/pager.nim | 61 | ||||
-rw-r--r-- | src/local/term.nim | 38 | ||||
-rw-r--r-- | src/utils/sandbox.nim | 2 |
5 files changed, 84 insertions, 31 deletions
diff --git a/adapter/img/stbi.nim b/adapter/img/stbi.nim index e4580d5c..f32c7f77 100644 --- a/adapter/img/stbi.nim +++ b/adapter/img/stbi.nim @@ -8,9 +8,11 @@ import utils/twtstr {.passc: "-fno-strict-aliasing".} {.passl: "-fno-strict-aliasing".} -{.compile: "stb_image.c".} +{.compile("stb_image.c", "-O3").} -type stbi_io_callbacks = object +{.push header: "stb_image.h".} + +type stbi_io_callbacks {.importc.} = object read: proc(user: pointer; data: ptr uint8; size: cint): cint {.cdecl.} skip: proc(user: pointer; n: cint) {.cdecl.} eof: proc(user: pointer): cint {.cdecl.} @@ -26,6 +28,8 @@ proc stbi_failure_reason(): cstring {.importc.} proc stbi_image_free(retval_from_stbi_load: pointer) {.importc.} +{.pop.} + proc myRead(user: pointer; data: ptr uint8; size: cint): cint {.cdecl.} = return cint(stdin.readBuffer(data, size)) @@ -41,20 +45,24 @@ proc myEof(user: pointer): cint {.cdecl.} = type stbi_write_func = proc(context, data: pointer; size: cint) {.cdecl.} +{.push header: "stb_image_write.h".} proc stbi_write_png_to_func(fun: stbi_write_func; context: pointer; w, h, comp: cint; data: pointer; stride_in_bytes: cint) {.importc.} proc stbi_write_bmp_to_func(fun: stbi_write_func; context: pointer; w, h, comp: cint; data: pointer) {.importc.} proc stbi_write_jpg_to_func(fun: stbi_write_func; context: pointer; w, h, comp: cint; data: pointer; quality: cint) {.importc.} +{.pop.} proc myWriteFunc(context, data: pointer; size: cint) {.cdecl.} = discard stdout.writeBuffer(data, size) +{.push header: "stb_image_resize.h".} proc stbir_resize_uint8(input_pixels: ptr uint8; input_w, input_h, input_stride_in_bytes: cint; output_pixels: ptr uint8; output_w, output_h, output_stride_in_bytes, num_channels: cint): cint {.importc.} +{.pop.} proc main() = enterNetworkSandbox() 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) diff --git a/src/utils/sandbox.nim b/src/utils/sandbox.nim index d4312a49..a700ea2e 100644 --- a/src/utils/sandbox.nim +++ b/src/utils/sandbox.nim @@ -127,7 +127,7 @@ elif defined(linux) and not disableSandbox: onSignal SIGSYS: discard sig raise newException(Defect, "Sandbox violation in network process") - let ctx = seccomp_init(SCMP_ACT_KILL_PROCESS) + let ctx = seccomp_init(SCMP_ACT_TRAP) doAssert pointer(ctx) != nil const allowList = [ cstring"close", "exit_group", # duh |