diff options
Diffstat (limited to 'boxdraw.c')
-rw-r--r-- | boxdraw.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/boxdraw.c b/boxdraw.c new file mode 100644 index 0000000..28a92d0 --- /dev/null +++ b/boxdraw.c | |||
@@ -0,0 +1,194 @@ | |||
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 | } | ||