boxdraw.c (6186B)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | // Copyright 2024 jack@pngu.org // SPDX-License-Identifier: MIT/X #include <X11/Xft/Xft.h> #include "st.h" #include "boxdraw_data.h" /* Rounded non-negative integers division of n / d */ #define DIV(n, d) (((n) + (d) / 2) / (d)) static Display *xdpy; static Colormap xcmap; static XftDraw *xd; static Visual *xvis; static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); static void drawboxlines(int, int, int, int, XftColor *, ushort); /* public API */ void boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) { xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; } int isboxdraw(Rune u) { Rune block = u & ~0xff; return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || (boxdraw_braille && block == 0x2800); } /* the "index" is actually the entire shape data encoded as ushort */ ushort boxdrawindex(const Glyph *g) { if (boxdraw_braille && (g->u & ~0xff) == 0x2800) return BRL | (uint8_t)g->u; if (boxdraw_bold && (g->mode & ATTR_BOLD)) return BDB | boxdata[(uint8_t)g->u]; return boxdata[(uint8_t)g->u]; } void drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, const XftGlyphFontSpec *specs, int len) { for ( ; len-- > 0; x += cw, specs++) drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); } /* implementation */ void drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) { ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ if (bd & (BDL | BDA)) { /* lines (light/double/heavy/arcs) */ drawboxlines(x, y, w, h, fg, bd); } else if (cat == BBD) { /* lower (8-X)/8 block */ int d = DIV((uint8_t)bd * h, 8); XftDrawRect(xd, fg, x, y + d, w, h - d); } else if (cat == BBU) { /* upper X/8 block */ XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); } else if (cat == BBL) { /* left X/8 block */ XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); } else if (cat == BBR) { /* right (8-X)/8 block */ int d = DIV((uint8_t)bd * w, 8); XftDrawRect(xd, fg, x + d, y, w - d, h); } else if (cat == BBQ) { /* Quadrants */ int w2 = DIV(w, 2), h2 = DIV(h, 2); if (bd & TL) XftDrawRect(xd, fg, x, y, w2, h2); if (bd & TR) XftDrawRect(xd, fg, x + w2, y, w - w2, h2); if (bd & BL) XftDrawRect(xd, fg, x, y + h2, w2, h - h2); if (bd & BR) XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); } else if (bd & BBS) { /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ int d = (uint8_t)bd; XftColor xfc; XRenderColor xrc = { .alpha = 0xffff }; xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); XftDrawRect(xd, &xfc, x, y, w, h); XftColorFree(xdpy, xvis, xcmap, &xfc); } else if (cat == BRL) { /* braille, each data bit corresponds to one dot at 2x4 grid */ int w1 = DIV(w, 2); int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); } } void drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) { /* s: stem thickness. width/8 roughly matches underscore thickness. */ /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ int mwh = MIN(w, h); int base_s = MAX(1, DIV(mwh, 8)); int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ /* The base length (per direction till edge) includes this square. */ int light = bd & (LL | LU | LR | LD); int double_ = bd & (DL | DU | DR | DD); if (light) { /* d: additional (negative) length to not-draw the center */ /* texel - at arcs and avoid drawing inside (some) doubles */ int arc = bd & BDA; int multi_light = light & (light - 1); int multi_double = double_ & (double_ - 1); /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ int d = arc || (multi_double && !multi_light) ? -s : 0; if (bd & LL) XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); if (bd & LU) XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); if (bd & LR) XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); if (bd & LD) XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); } /* double lines - also align with light to form heavy when combined */ if (double_) { /* * going clockwise, for each double-ray: p is additional length * to the single-ray nearer to the previous direction, and n to * the next. p and n adjust from the base length to lengths * which consider other doubles - shorter to avoid intersections * (p, n), or longer to draw the far-corner texel (n). */ int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; if (dl) { int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); } if (du) { int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); } if (dr) { int p = du ? -s : 0, n = dd ? -s : du ? s : 0; XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); } if (dd) { int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); } } } |