diff options
author | bptato <nincsnevem662@gmail.com> | 2024-09-01 01:03:50 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-09-01 01:46:38 +0200 |
commit | 55cfd29e961488a8c1ed9eb7801d237d27bc86c7 (patch) | |
tree | c74569e15ca72d777eadcfd19a0203cbb76c3e3f /adapter/img | |
parent | e9466c4c436f964b53034e28356aa3f5c957a068 (diff) | |
download | chawan-55cfd29e961488a8c1ed9eb7801d237d27bc86c7.tar.gz |
canvas: move to separate CGI script
* stream: and passFd is now client-based, and accessible for buffers * Bitmap's width & height is now int, not uint64 * no more non-network Bitmap special case in the pager for canvas I just shoehorned it into the static image model, so it still doesn't render changes after page load. But at least now it doesn't crash the browser.
Diffstat (limited to 'adapter/img')
-rw-r--r-- | adapter/img/canvas.c | 5 | ||||
-rw-r--r-- | adapter/img/canvas.nim | 141 | ||||
-rw-r--r-- | adapter/img/jebp.nim | 5 |
3 files changed, 148 insertions, 3 deletions
diff --git a/adapter/img/canvas.c b/adapter/img/canvas.c new file mode 100644 index 00000000..9e386ca1 --- /dev/null +++ b/adapter/img/canvas.c @@ -0,0 +1,5 @@ +#define STBI_ONLY_PNG +#define STBI_NO_STDIO +#define STBI_NO_LINEAR +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" diff --git a/adapter/img/canvas.nim b/adapter/img/canvas.nim new file mode 100644 index 00000000..2182e1d9 --- /dev/null +++ b/adapter/img/canvas.nim @@ -0,0 +1,141 @@ +# Very simple canvas renderer. At the moment, it uses an undocumented binary +# protocol for reading commands, and renders it whenever stdin is closed. +# So for now, it can only really render a single frame. +# +# It uses unifont for rendering text - currently I just store it as PNG +# and read it with stbi. (TODO: try switching to a more efficient format +# like qemacs fbf.) + +import std/os +import std/posix +import std/strutils + +import css/cssvalues +import img/bitmap +import img/painter +import img/path +import io/bufreader +import io/dynstream +import types/color +import types/line +import utils/sandbox + +{.compile: "canvas.c".} + +{.passc: "-I" & currentSourcePath().parentDir().} + +{.push header: "stb_image.h".} +proc stbi_load_from_memory(buffer: ptr uint8; len: cint; x, y, comp: ptr cint; + req_comp: cint): ptr uint8 +proc stbi_image_free(retval_from_stbi_load: pointer) +{.pop.} + +const unifont = readFile"res/unifont_jp-15.0.05.png" +proc loadUnifont(unifont: string): ImageBitmap = + var width, height, comp: cint + let p = stbi_load_from_memory(cast[ptr uint8](unsafeAddr unifont[0]), + cint(unifont.len), addr width, addr height, addr comp, 4) + let len = width * height + let bitmap = ImageBitmap( + px: cast[seq[RGBAColorBE]](newSeqUninitialized[uint32](len)), + width: int(width), + height: int(height) + ) + copyMem(addr bitmap.px[0], p, len) + stbi_image_free(p) + return bitmap + +proc main() = + enterNetworkSandbox() + let os = newPosixStream(STDOUT_FILENO) + let ps = newPosixStream(STDIN_FILENO) + if getEnv("MAPPED_URI_SCHEME") != "img-codec+x-cha-canvas": + os.write("Cha-Control: ConnectionError 1 wrong scheme\n") + quit(1) + case getEnv("MAPPED_URI_PATH") + of "decode": + let headers = getEnv("REQUEST_HEADERS") + for hdr in headers.split('\n'): + if hdr.strip() == "Cha-Image-Info-Only: 1": + #TODO this is a hack... + # basically, we eat & discard all data from the buffer so it gets saved + # to a cache file. then, actually render when the pager asks us to + # do so. + # obviously this is highly sub-optimal; a better solution would be to + # leave stdin open & pass down the stream id from the buffer. (but then + # you have to save canvas output too, so it doesn't have to be + # re-coded, and handle that case in encoders... or implement on-demand + # multi-frame output.) + os.write("\n") + discard ps.recvAll() + quit(0) + var cmd: PaintCommand + var width: int + var height: int + ps.withPacketReader r: + r.sread(cmd) + if cmd != pcSetDimensions: + os.write("Cha-Control: ConnectionError 1 wrong dimensions\n") + quit(1) + r.sread(width) + r.sread(height) + os.write("Cha-Image-Dimensions: " & $width & "x" & $height & "\n\n") + let bmp = newBitmap(width, height) + var alive = true + while alive: + try: + ps.withPacketReader r: + r.sread(cmd) + case cmd + of pcSetDimensions: + alive = false + of pcFillRect, pcStrokeRect: + var x1, y1, x2, y2: int + var color: ARGBColor + r.sread(x1) + r.sread(y1) + r.sread(x2) + r.sread(y2) + r.sread(color) + if cmd == pcFillRect: + bmp.fillRect(x1, y1, x2, y2, color) + else: + bmp.strokeRect(x1, y1, x2, y2, color) + of pcFillPath: + var lines: PathLines + var color: ARGBColor + var fillRule: CanvasFillRule + r.sread(lines) + r.sread(color) + r.sread(fillRule) + bmp.fillPath(lines, color, fillRule) + of pcStrokePath: + var lines: seq[Line] + var color: ARGBColor + r.sread(lines) + r.sread(color) + bmp.strokePath(lines, color) + of pcFillText, pcStrokeText: + if unifontBitmap == nil: + unifontBitmap = loadUnifont(unifont) + var text: string + var x, y: float64 + var color: ARGBColor + var align: CSSTextAlign + r.sread(text) + r.sread(x) + r.sread(y) + r.sread(color) + r.sread(align) + if cmd == pcFillText: + bmp.fillText(text, x, y, color, align) + else: + bmp.strokeText(text, x, y, color, align) + except EOFError, ErrorConnectionReset, ErrorBrokenPipe: + break + os.sendDataLoop(addr bmp.px[0], bmp.px.len * sizeof(bmp.px[0])) + of "encode": + os.write("Cha-Control: ConnectionError 1 not supported\n") + quit(1) + +main() diff --git a/adapter/img/jebp.nim b/adapter/img/jebp.nim index 03d72d59..afeb283e 100644 --- a/adapter/img/jebp.nim +++ b/adapter/img/jebp.nim @@ -6,9 +6,6 @@ import std/strutils import utils/sandbox import utils/twtstr -{.passc: "-fno-strict-aliasing".} -{.passl: "-fno-strict-aliasing".} - {.compile: "jebp.c".} when sizeof(cint) < 4: @@ -61,10 +58,12 @@ proc myRead(data: pointer; size: csize_t; user: pointer): csize_t {.cdecl.} = n += csize_t(i) return n +{.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 writeAll(data: pointer; size: int) = var n = 0 |