diff --git a/kitty.c b/kitty.c index 9d119ff..135711d 100644 --- a/kitty.c +++ b/kitty.c @@ -1941,6 +1941,8 @@ kitty_absorb_diacritic(Glyph *gp, Rune u) /* rendering */ /* ------------------------------------------------------------------ */ +static int kitty_cmp(const void *a, const void *b); + /* nearest-neighbour + bilinear scaling of a source rect to w x h RGBA */ static unsigned char * kitty_scale(const unsigned char *src, int sw, int sh, int stride, @@ -2063,6 +2065,96 @@ kitty_prepare(KittyPlacement *p, int *outw, int *outh) return 1; } +/* like kitty_blit but restricted to the cell span [cx1, cx2) on row cy */ +static void +kitty_blit_clipped(KittyPlacement *p, int px, int py, int cx1, int cx2, int cy) +{ + Picture src, dstpic; + XRenderPictFormat *fmt; + XRenderPictureAttributes pa; + int w, h, bx, by; + int x0, x1, y0, y1; /* clip rect in window pixels */ + int ix0, iy0; /* image origin in window pixels */ + int sxo, syo, cw, chh; + + if (!kitty_prepare(p, &w, &h)) + return; + + kitty_border(&bx, &by); + ix0 = bx + px * win.cw + p->xoff; + iy0 = by + (py - kitty_scr()) * win.ch + p->yoff; + + x0 = KMAX(ix0, bx + cx1 * win.cw); + x1 = KMIN(ix0 + w, bx + cx2 * win.cw); + y0 = KMAX(iy0, by + cy * win.ch); + y1 = KMIN(iy0 + h, by + (cy + 1) * win.ch); + x1 = KMIN(x1, bx + win.tw); + y1 = KMIN(y1, by + win.th); + if (x1 <= x0 || y1 <= y0) + return; + + sxo = x0 - ix0; + syo = y0 - iy0; + cw = x1 - x0; + chh = y1 - y0; + + memset(&pa, 0, sizeof pa); + fmt = XRenderFindStandardFormat(xw.dpy, PictStandardARGB32); + if (!fmt) + return; + src = XRenderCreatePicture(xw.dpy, (Drawable)p->pixmap, fmt, 0, &pa); + fmt = XRenderFindVisualFormat(xw.dpy, xw.vis); + if (fmt) { + dstpic = XRenderCreatePicture(xw.dpy, xw.buf, fmt, 0, &pa); + XRenderComposite(xw.dpy, PictOpOver, src, None, dstpic, + sxo, syo, 0, 0, x0, y0, cw, chh); + XRenderFreePicture(xw.dpy, dstpic); + } + XRenderFreePicture(xw.dpy, src); +} + +/* + * Composite the negative z-index images covering the cell span + * [x1, x2) on row y. Called after the background has been painted but + * before the glyphs, so that text appears on top of the image. + */ +void +kitty_draw_under_cells(int x1, int y, int x2) +{ + KittyPlacement **list = NULL, *p; + int n = 0, cap = 0, i; + int s = IS_SET(MODE_ALTSCREEN) ? 1 : 0; + int scr = kitty_scr(); + + for (p = placements[s]; p; p = p->next) { + int px, py; + if (p->virt || p->z >= 0) + continue; + if (!kitty_resolve_pos(p, &px, &py, 0)) + continue; + py -= scr; + if (px >= x2 || px + p->cols <= x1 || py > y || py + p->rows <= y) + continue; + if (n >= cap) { + int ncap = cap ? cap * 2 : 8; + KittyPlacement **t = realloc(list, ncap * sizeof *t); + if (!t) + break; + list = t; + cap = ncap; + } + list[n++] = p; + } + if (n > 1) + qsort(list, n, sizeof *list, kitty_cmp); + for (i = 0; i < n; i++) { + int px, py; + if (kitty_resolve_pos(list[i], &px, &py, 0)) + kitty_blit_clipped(list[i], px, py, x1, x2, y); + } + free(list); +} + static void kitty_blit(KittyPlacement *p, int px, int py) { @@ -2401,8 +2493,12 @@ kitty_draw_placeholders(void) } } -void -kitty_draw(void) +/* + * Draw the visible placements whose z-index falls in [zlo, zhi]. + * Negative z-index images are drawn under the text (see kitty_draw_under). + */ +static void +kitty_draw_range(int zlo, int zhi) { KittyPlacement **list = NULL, *p; int n = 0, cap = 0, i; @@ -2416,6 +2512,8 @@ kitty_draw(void) int px, py; if (p->virt) continue; + if (p->z < zlo || p->z > zhi) + continue; if (!kitty_resolve_pos(p, &px, &py, 0)) continue; if (px >= term.col || py - scr >= term.row || py - scr + p->rows <= 0) @@ -2441,11 +2539,38 @@ kitty_draw(void) } free(list); +} + +void +kitty_draw(void) +{ + kitty_draw_range(0, INT_MAX); /* images displayed via Unicode placeholders */ kitty_draw_placeholders(); } +/* is the cell (x, y) covered by an image drawn under the text? */ +int +kitty_cell_under(int x, int y) +{ + KittyPlacement *p; + int s = IS_SET(MODE_ALTSCREEN) ? 1 : 0; + int scr = kitty_scr(); + + for (p = placements[s]; p; p = p->next) { + int px, py; + if (p->virt || p->z >= 0) + continue; + if (!kitty_resolve_pos(p, &px, &py, 0)) + continue; + py -= scr; + if (x >= px && x < px + p->cols && y >= py && y < py + p->rows) + return 1; + } + return 0; +} + /* ------------------------------------------------------------------ */ /* terminal lifecycle hooks */ /* ------------------------------------------------------------------ */ diff --git a/kitty.h b/kitty.h index 8e1cd0c..e2aac01 100644 --- a/kitty.h +++ b/kitty.h @@ -76,6 +76,8 @@ typedef struct KittyPlacement { /* entry points used by st.c / x.c */ void kitty_apc(const char *buf, size_t len); void kitty_draw(void); +void kitty_draw_under_cells(int x1, int y, int x2); +int kitty_cell_under(int x, int y); void kitty_scroll(int top, int bot, int n, int save); void kitty_deleteplacements(int alt); void kitty_clearregion(int x1, int y1, int x2, int y2); diff --git a/x.c b/x.c index 3330a94..becc042 100644 --- a/x.c +++ b/x.c @@ -3065,6 +3065,11 @@ xdrawline(Line line, int x1, int y1, int x2) /* Draw line in 2 passes: background and foreground. This way wide glyphs won't get truncated (#223) */ for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) { + #if KITTY_GRAPHICS_PATCH + /* negative z-index images sit between the background and the text */ + if (dmode == DRAW_FG) + kitty_draw_under_cells(x1, y1, x2); + #endif // KITTY_GRAPHICS_PATCH specs = xw.specbuf; numspecs = numspecs_cached; i = ox = 0;