commit 118e965d0c8bd9c29b5e6bcba3ffcc1dd41064a7
parent dd8675943d2e6e1eff15d3ac3aac6e5e5643582b
Author: veltza <106755522+veltza@users.noreply.github.com>
Date: Wed, 17 Apr 2024 19:04:27 +0300
sixel: add support for fully transparent bg (P2=1) (#132)
P2 selects how the terminal draws the background color.
P2 Meaning
0 or 2 (default) Pixel positions specified as 0 are set to the
current background color.
1 Pixel positions specified as 0 remain at their
current color.
Both modes are now supported.
Ref. https://www.vt100.net/docs/vt3xx-gp/chapter14.html
Diffstat:
| M | patch/reflow.c | | | 15 | ++++----------- |
| M | sixel.c | | | 40 | +++++++++++++++++++++++++++++++++++++++- |
| M | sixel.h | | | 4 | +++- |
| M | st.c | | | 32 | +++++++++++++++++--------------- |
| M | st.h | | | 2 | ++ |
| M | x.c | | | 25 | ++++++++++++++++++++----- |
6 files changed, 85 insertions(+), 33 deletions(-)
diff --git a/patch/reflow.c b/patch/reflow.c
@@ -81,7 +81,7 @@ treflow(int col, int row)
int oce, nce, bot, scr;
int ox = 0, oy = -term.histf, nx = 0, ny = -1, len;
int cy = -1; /* proxy for new y coordinate of cursor */
- int buflen, nlines, del;
+ int buflen, nlines;
Line *buf, bufline, line;
#if SIXEL_PATCH
ImageList *im, *next;
@@ -219,21 +219,14 @@ treflow(int col, int row)
}
}
- /* expand images into new text cells or
- * delete images if there is text behind them */
+ /* expand images into new text cells */
for (im = term.images; im; im = next) {
next = im->next;
if (im->x < col) {
line = TLINE(im->y);
x2 = MIN(im->x + im->cols, col);
- for (del = 0, x = im->x; x < x2; x++) {
- if ((del = line[x].mode & ATTR_SET))
- break;
- line[x].u = ' ';
- line[x].mode = ATTR_SIXEL;
- }
- if (del)
- delete_image(im);
+ for (x = im->x; x < x2; x++)
+ line[x].mode |= ATTR_SIXEL;
}
}
#endif // SIXEL_PATCH
diff --git a/sixel.c b/sixel.c
@@ -66,6 +66,8 @@ delete_image(ImageList *im)
im->next->prev = im->prev;
if (im->pixmap)
XFreePixmap(xw.dpy, (Drawable)im->pixmap);
+ if (im->clipmask)
+ XFreePixmap(xw.dpy, (Drawable)im->clipmask);
free(im->pixels);
free(im);
}
@@ -217,6 +219,7 @@ sixel_image_deinit(sixel_image_t *image)
int
sixel_parser_init(sixel_state_t *st,
+ int transparent,
sixel_color_t fgcolor, sixel_color_t bgcolor,
unsigned char use_private_register,
int cell_width, int cell_height)
@@ -232,6 +235,7 @@ sixel_parser_init(sixel_state_t *st,
st->attributed_pad = 1;
st->attributed_ph = 0;
st->attributed_pv = 0;
+ st->transparent = transparent;
st->repeat_count = 1;
st->color_index = 16;
st->grid_width = cell_width;
@@ -240,7 +244,7 @@ sixel_parser_init(sixel_state_t *st,
st->param = 0;
/* buffer initialization */
- status = sixel_image_init(&st->image, 1, 1, fgcolor, bgcolor, use_private_register);
+ status = sixel_image_init(&st->image, 1, 1, fgcolor, transparent ? 0 : bgcolor, use_private_register);
return status;
}
@@ -304,8 +308,10 @@ sixel_parser_finalize(sixel_state_t *st, ImageList **newimages, int cx, int cy,
im->height = MIN(h - ch * i, ch);
im->pixels = malloc(im->width * im->height * 4);
im->pixmap = NULL;
+ im->clipmask = NULL;
im->cw = cw;
im->ch = ch;
+ im->transparent = st->transparent;
}
if (!im || !im->pixels) {
for (im = *newimages; im; im = next) {
@@ -652,3 +658,35 @@ sixel_parser_deinit(sixel_state_t *st)
if (st)
sixel_image_deinit(&st->image);
}
+
+Pixmap
+sixel_create_clipmask(char *pixels, int width, int height)
+{
+ char c, *clipdata, *dst;
+ int b, i, n, y, w;
+ int msb = (XBitmapBitOrder(xw.dpy) == MSBFirst);
+ sixel_color_t *src = (sixel_color_t *)pixels;
+ Pixmap clipmask;
+
+ clipdata = dst = malloc((width+7)/8 * height);
+ if (!clipdata)
+ return (Pixmap)None;
+
+ for (y = 0; y < height; y++) {
+ for (w = width; w > 0; w -= n) {
+ n = MIN(w, 8);
+ if (msb) {
+ for (b = 0x80, c = 0, i = 0; i < n; i++, b >>= 1)
+ c |= (*src++) ? b : 0;
+ } else {
+ for (b = 0x01, c = 0, i = 0; i < n; i++, b <<= 1)
+ c |= (*src++) ? b : 0;
+ }
+ *dst++ = c;
+ }
+ }
+
+ clipmask = XCreateBitmapFromData(xw.dpy, xw.win, clipdata, width, height);
+ free(clipdata);
+ return clipmask;
+}
diff --git a/sixel.h b/sixel.h
@@ -39,6 +39,7 @@ typedef struct parser_context {
int attributed_pad;
int attributed_ph;
int attributed_pv;
+ int transparent;
int repeat_count;
int color_index;
int bgindex;
@@ -52,10 +53,11 @@ typedef struct parser_context {
void scroll_images(int n);
void delete_image(ImageList *im);
-int sixel_parser_init(sixel_state_t *st, sixel_color_t fgcolor, sixel_color_t bgcolor, unsigned char use_private_register, int cell_width, int cell_height);
+int sixel_parser_init(sixel_state_t *st, int transparent, sixel_color_t fgcolor, sixel_color_t bgcolor, unsigned char use_private_register, int cell_width, int cell_height);
int sixel_parser_parse(sixel_state_t *st, const unsigned char *p, size_t len);
int sixel_parser_set_default_color(sixel_state_t *st);
int sixel_parser_finalize(sixel_state_t *st, ImageList **newimages, int cx, int cy, int cw, int ch);
void sixel_parser_deinit(sixel_state_t *st);
+Pixmap sixel_create_clipmask(char *pixels, int width, int height);
#endif
diff --git a/st.c b/st.c
@@ -2714,14 +2714,18 @@ strhandle(void)
y1 = newimages->y;
x2 = x1 + newimages->cols;
y2 = y1 + numimages;
- for (tail = NULL, im = term.images; im; im = next) {
- next = im->next;
- if (im->x >= x1 && im->x + im->cols <= x2 &&
- im->y >= y1 && im->y <= y2) {
- delete_image(im);
- continue;
+ if (newimages->transparent) {
+ for (tail = term.images; tail && tail->next; tail = tail->next);
+ } else {
+ for (tail = NULL, im = term.images; im; im = next) {
+ next = im->next;
+ if (im->x >= x1 && im->x + im->cols <= x2 &&
+ im->y >= y1 && im->y <= y2) {
+ delete_image(im);
+ continue;
+ }
+ tail = im;
}
- tail = im;
}
if (tail) {
tail->next = newimages;
@@ -2747,8 +2751,7 @@ strhandle(void)
line = term.line[term.c.y];
}
for (x = im->x; x < x2; x++) {
- line[x].u = ' ';
- line[x].mode = ATTR_SIXEL;
+ line[x].mode |= ATTR_SIXEL;
}
term.dirty[MIN(im->y, term.row-1)] = 1;
if (!IS_SET(MODE_SIXEL_SDM) && i < numimages-1) {
@@ -3097,7 +3100,7 @@ tcontrolcode(uchar ascii)
void
dcshandle(void)
{
- int bgcolor;
+ int bgcolor, transparent;
unsigned char r, g, b, a = 255;
switch (csiescseq.mode[0]) {
@@ -3119,6 +3122,7 @@ dcshandle(void)
break;
#endif // SYNC_PATCH
case 'q': /* DECSIXEL */
+ transparent = (csiescseq.narg >= 2 && csiescseq.arg[1] == 1);
if (IS_TRUECOL(term.c.attr.bg)) {
r = term.c.attr.bg >> 16 & 255;
g = term.c.attr.bg >> 8 & 255;
@@ -3129,7 +3133,7 @@ dcshandle(void)
a = dc.col[defaultbg].pixel >> 24 & 255;
}
bgcolor = a << 24 | r << 16 | g << 8 | b;
- if (sixel_parser_init(&sixel_st, (255 << 24), bgcolor, 1, win.cw, win.ch) != 0)
+ if (sixel_parser_init(&sixel_st, transparent, (255 << 24), bgcolor, 1, win.cw, win.ch) != 0)
perror("sixel_parser_init() failed");
term.mode |= MODE_SIXEL;
break;
@@ -3632,10 +3636,8 @@ tresize(int col, int row)
line = term.line[im->y];
#endif // SCROLLBACK_PATCH
x2 = MIN(im->x + im->cols, term.col);
- for (x = im->x; x < x2; x++) {
- line[x].u = ' ';
- line[x].mode = ATTR_SIXEL;
- }
+ for (x = im->x; x < x2; x++)
+ line[x].mode |= ATTR_SIXEL;
}
tswapscreen();
}
diff --git a/st.h b/st.h
@@ -77,6 +77,7 @@ typedef struct _ImageList {
struct _ImageList *next, *prev;
unsigned char *pixels;
void *pixmap;
+ void *clipmask;
int width;
int height;
int x;
@@ -87,6 +88,7 @@ typedef struct _ImageList {
int cols;
int cw;
int ch;
+ int transparent;
} ImageList;
#endif // SIXEL_PATCH
diff --git a/x.c b/x.c
@@ -325,7 +325,10 @@ zoomabs(const Arg *arg)
for (im = term.images; im; im = im->next) {
if (im->pixmap)
XFreePixmap(xw.dpy, (Drawable)im->pixmap);
+ if (im->clipmask)
+ XFreePixmap(xw.dpy, (Drawable)im->clipmask);
im->pixmap = NULL;
+ im->clipmask = NULL;
}
#endif // SIXEL_PATCH
@@ -3138,7 +3141,7 @@ xfinishdraw(void)
XGCValues gcvalues;
GC gc;
int width, height;
- int x, x2, del;
+ int x, x2, del, destx, desty;
Line line;
#endif // SIXEL_PATCH
@@ -3161,6 +3164,8 @@ xfinishdraw(void)
DefaultDepth(xw.dpy, xw.scr)
#endif // ALPHA_PATCH
);
+ if (!im->pixmap)
+ continue;
if (win.cw == im->cw && win.ch == im->ch) {
XImage ximage = {
.format = ZPixmap,
@@ -3181,12 +3186,15 @@ xfinishdraw(void)
#endif // ALPHA_PATCH
};
XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height);
+ if (im->transparent)
+ im->clipmask = (void *)sixel_create_clipmask((char *)im->pixels, width, height);
} else {
origin = imlib_create_image_using_data(im->width, im->height, (DATA32 *)im->pixels);
if (!origin)
continue;
imlib_context_set_image(origin);
imlib_image_set_has_alpha(1);
+ imlib_context_set_anti_alias(im->transparent ? 0 : 1); /* anti-aliasing messes up the clip mask */
scaled = imlib_create_cropped_scaled_image(0, 0, im->width, im->height, width, height);
imlib_free_image_and_decache();
if (!scaled)
@@ -3212,6 +3220,8 @@ xfinishdraw(void)
#endif // ALPHA_PATCH
};
XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height);
+ if (im->transparent)
+ im->clipmask = (void *)sixel_create_clipmask((char *)imlib_image_get_data_for_reading_only(), width, height);
imlib_free_image_and_decache();
}
}
@@ -3240,12 +3250,17 @@ xfinishdraw(void)
gcvalues.graphics_exposures = False;
gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, &gcvalues);
#if ANYSIZE_PATCH
- XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0,
- width, height, win.hborderpx + im->x * win.cw, win.vborderpx + im->y * win.ch);
+ destx = win.hborderpx + im->x * win.cw;
+ desty = win.vborderpx + im->y * win.ch;
#else
- XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0,
- width, height, borderpx + im->x * win.cw, borderpx + im->y * win.ch);
+ destx = borderpx + im->x * win.cw;
+ desty = borderpx + im->y * win.ch;
#endif // ANYSIZE_PATCH
+ if (im->clipmask) {
+ XSetClipMask(xw.dpy, gc, (Drawable)im->clipmask);
+ XSetClipOrigin(xw.dpy, gc, destx, desty);
+ }
+ XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, width, height, destx, desty);
XFreeGC(xw.dpy, gc);
}
#endif // SIXEL_PATCH