diff options
author | bptato <nincsnevem662@gmail.com> | 2024-09-24 23:25:07 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-09-24 23:55:43 +0200 |
commit | 1c9277343140effcc9eee8845757afd1eed4f4bd (patch) | |
tree | 7a902dc2aebce67ee5749442129176b0683cf60f /src | |
parent | 0c738c94e14c213562f69ff6e376c19fb0487201 (diff) | |
download | chawan-1c9277343140effcc9eee8845757afd1eed4f4bd.tar.gz |
sixel: support transparency
Sixel can only represent transparency for fully transparent (alpha = 0) and fully opaque (alpha = 255) pixels, i.e. we would have to do blending ourselves to do this "properly". But what do you even blend? Background color? Images? Clearly you can't do text... So instead of going down the blending route, we now just approximate the 8-bit channel with Sixel's 1-bit channel and then patch it up with dither. It does look a bit weird, but it's not *that* bad, especially compared to the previous strategy of "blend with some color which hopefully happens to be the background color" (it rarely was). Note that this requires us to handle transparent images specially in term. That is, for opaque ones, we can leave out the "clear cells affected by image" part, but for transparent ones, we must clear the entire image every time.
Diffstat (limited to 'src')
-rw-r--r-- | src/layout/renderdocument.nim | 4 | ||||
-rw-r--r-- | src/local/container.nim | 2 | ||||
-rw-r--r-- | src/local/pager.nim | 8 | ||||
-rw-r--r-- | src/local/term.nim | 38 | ||||
-rw-r--r-- | src/server/buffer.nim | 4 | ||||
-rw-r--r-- | src/types/color.nim | 2 |
6 files changed, 34 insertions, 24 deletions
diff --git a/src/layout/renderdocument.nim b/src/layout/renderdocument.nim index 76042ae2..9622d305 100644 --- a/src/layout/renderdocument.nim +++ b/src/layout/renderdocument.nim @@ -231,7 +231,6 @@ type width*: int height*: int bmp*: NetworkBitmap - bgcolor*: RGBColor AbsolutePos = object offset: Offset @@ -380,8 +379,7 @@ proc renderInlineFragment(grid: var FlexibleGrid; state: var RenderState; y: (offset.y div state.attrs.ppl).toInt, width: atom.size.w.toInt, height: atom.size.h.toInt, - bmp: atom.bmp, - bgcolor: bgcolor0.toRGBColor() + bmp: atom.bmp )) if fragment.computed{"position"} != PositionStatic: if fragment.splitType != {stSplitStart, stSplitEnd}: diff --git a/src/local/container.nim b/src/local/container.nim index 6bdfe64b..5632bd88 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -122,6 +122,8 @@ type offx*: int # same as CanvasImage.offx dispw*: int # same as CanvasImage.dispw erry*: int # same as CanvasImage.offy % 6 + # whether the image has transparency, *disregarding the last row* + transparent*: bool Container* = ref object of RootObj # note: this is not the same as source.request.url (but should be synced diff --git a/src/local/pager.nim b/src/local/pager.nim index ec242472..e8034af7 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -586,7 +586,6 @@ proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap; url = newURL("img-codec+x-sixel:encode").get headers.add("Cha-Image-Sixel-Halfdump", "1") headers.add("Cha-Image-Sixel-Palette", $pager.term.sixelRegisterNum) - headers.add("Cha-Image-Background-Color", $image.bgcolor) headers.add("Cha-Image-Offset", $offx & 'x' & $erry) headers.add("Cha-Image-Crop-Width", $dispw) of imKitty: @@ -630,6 +629,11 @@ proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap; cachedImage.data = blob cachedImage.state = cisLoaded cachedImage.cacheId = cacheId + if imageMode == imSixel and 4 < blob.size: + #TODO this should be a response header, but loader can't send us + # those yet... + let u = cast[ptr UncheckedArray[uint8]](blob.buffer)[4] + cachedImage.transparent = u == 1 ) ) container.cachedImages.add(cachedImage) @@ -660,7 +664,7 @@ proc initImages(pager: Pager; container: Container) = let canvasImage = pager.term.loadImage(cached.data, container.process, imageId, image.x - container.fromx, image.y - container.fromy, image.width, image.height, image.x, image.y, pager.bufWidth, - pager.bufHeight, erry, offx, dispw) + pager.bufHeight, erry, offx, dispw, cached.transparent) if canvasImage != nil: newImages.add(canvasImage) pager.term.clearImages(pager.bufHeight) diff --git a/src/local/term.nim b/src/local/term.nim index 0e245256..3f38193b 100644 --- a/src/local/term.nim +++ b/src/local/term.nim @@ -73,6 +73,7 @@ type damaged: bool marked*: bool dead: bool + transparent: bool # note: this is only set in outputSixelImage kittyId: int # 0 if kitty erry: int @@ -735,7 +736,12 @@ proc checkImageDamage*(term: Terminal; maxh: int) = let mx = image.x + (image.dispw - image.offx) div term.attrs.ppc for y in max(image.y, 0) ..< ey0: let od = term.lineDamage[y] - if od < mx: + if image.transparent and od > image.x: + image.damaged = true + if od < mx: + # damage starts inside this image; move it to its beginning. + term.lineDamage[y] = image.x + elif not image.transparent and od < mx: image.damaged = true if y >= ey1: break @@ -752,7 +758,8 @@ proc checkImageDamage*(term: Terminal; maxh: int) = term.lineDamage[y] = mx proc loadImage*(term: Terminal; data: Blob; pid, imageId, x, y, width, height, - rx, ry, maxw, maxh, erry, offx, dispw: int): CanvasImage = + rx, ry, maxw, maxh, erry, offx, dispw: int; transparent: bool): + CanvasImage = if (let image = term.findImage(pid, imageId, rx, ry, width, height, erry, offx, dispw); image != nil): # reuse image on screen @@ -777,7 +784,8 @@ proc loadImage*(term: Terminal; data: Blob; pid, imageId, x, y, width, height, ry: ry, width: width, height: height, - erry: erry + erry: erry, + transparent: transparent ) if term.positionImage(image, x, y, maxw, maxh): return image @@ -796,26 +804,22 @@ proc outputSixelImage(term: Terminal; x, y: int; image: CanvasImage; let offy = image.offy let dispw = image.dispw let disph = image.disph - var outs = term.cursorGoto(x, y) let realw = dispw - offx let realh = disph - offy - # set transparency if we want to draw a non-6-divisible number - # of rows; omit it otherwise, for then some terminals (e.g. foot) - # handle the image more efficiently - let trans = realh mod 6 != 0 - outs &= DCS & "0;" & $int(trans) & 'q' - # set raster attributes - outs &= "\"1;1;" & $realw & ';' & $realh - if data.len < 4: # bounds check - outs &= ST - term.write(outs) + if data.len < 5: # bounds check return let sraLen = int(data.getU32BE(0)) - let preludeLen = sraLen + 4 + let preludeLen = sraLen + 5 if preludeLen > data.len: - outs &= ST - term.write(outs) return + var outs = term.cursorGoto(x, y) + # set transparency if we want to draw a non-6-divisible number + # of rows *or* the image is transparent; omit it otherwise, for then + # some terminals (e.g. foot) handle the image more efficiently + let trans = realh mod 6 != 0 or image.transparent + outs &= DCS & "0;" & $int(trans) & "q" + # set raster attributes + outs &= "\"1;1;" & $realw & ';' & $realh term.write(outs) term.write(data.toOpenArray(4, preludeLen - 1)) let lookupTableLen = int(data.getU32BE(data.len - 4)) diff --git a/src/server/buffer.nim b/src/server/buffer.nim index c5d00655..71eef321 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -1615,8 +1615,10 @@ proc getLines*(buffer: Buffer; w: Slice[int]): GetLinesResult {.proxy.} = result.numLines = buffer.lines.len result.bgcolor = buffer.bgcolor if buffer.config.images: + let ppl = buffer.attrs.ppl for image in buffer.images: - if image.y <= w.b and image.y + image.height >= w.a: + let ey = image.y + (image.height + ppl - 1) div ppl # ceil + if image.y <= w.b and ey >= w.a: result.images.add(image) proc markURL*(buffer: Buffer; schemes: seq[string]) {.proxy.} = diff --git a/src/types/color.nim b/src/types/color.nim index ab8d4984..20de1fcc 100644 --- a/src/types/color.nim +++ b/src/types/color.nim @@ -293,7 +293,7 @@ func fastmul*(c, ca: uint32): uint32 = return ga or (rb shr 8) # fastmul, but preserves alpha -func fastmul1(c, ca: uint32): uint32 = +func fastmul1*(c, ca: uint32): uint32 = let u = c var rb = u and 0x00FF00FFu32 rb *= ca |