st

git clone git://mattcarlson.org/repos/st.git
Log | Files | Refs

boxdraw.c (6237B)


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