ttext-common.c - ltkx - GUI toolkit for X11 (WIP)
HTML git clone git://lumidify.org/ltkx.git
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
ttext-common.c (10432B)
---
1 /*
2 * This file is part of the Lumidify ToolKit (LTK)
3 * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <limits.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
31 #include <fontconfig/fontconfig.h>
32 #include "khash.h"
33 #include <fribidi.h>
34 #include <harfbuzz/hb.h>
35 #include <harfbuzz/hb-ot.h>
36 #include "text-common.h"
37 #include "ltk.h"
38
39 extern Ltk *ltk_global;
40
41 /* These unicode routines are taken from
42 * https://github.com/JeffBezanson/cutef8 */
43
44 /* is c the start of a utf8 sequence? */
45 #define isutf(c) (((c)&0xC0)!=0x80)
46
47 static const uint32_t offsetsFromUTF8[6] = {
48 0x00000000UL, 0x00003080UL, 0x000E2080UL,
49 0x03C82080UL, 0xFA082080UL, 0x82082080UL
50 };
51
52 /* next character without NUL character terminator */
53 uint32_t u8_nextmemchar(const char *s, size_t *i)
54 {
55 uint32_t ch = 0;
56 size_t sz = 0;
57 do {
58 ch <<= 6;
59 ch += (unsigned char)s[(*i)++];
60 sz++;
61 } while (!isutf(s[*i]));
62 ch -= offsetsFromUTF8[sz-1];
63
64 return ch;
65 }
66
67 /* number of characters in NUL-terminated string */
68 size_t u8_strlen(const char *s)
69 {
70 size_t count = 0;
71 size_t i = 0, lasti;
72
73 while (1) {
74 lasti = i;
75 while (s[i] > 0)
76 i++;
77 count += (i-lasti);
78 if (s[i++]==0) break;
79 (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
80 count++;
81 }
82 return count;
83 }
84
85 size_t u8_wc_toutf8(char *dest, uint32_t ch)
86 {
87 if (ch < 0x80) {
88 dest[0] = (char)ch;
89 return 1;
90 }
91 if (ch < 0x800) {
92 dest[0] = (ch>>6) | 0xC0;
93 dest[1] = (ch & 0x3F) | 0x80;
94 return 2;
95 }
96 if (ch < 0x10000) {
97 dest[0] = (ch>>12) | 0xE0;
98 dest[1] = ((ch>>6) & 0x3F) | 0x80;
99 dest[2] = (ch & 0x3F) | 0x80;
100 return 3;
101 }
102 if (ch < 0x110000) {
103 dest[0] = (ch>>18) | 0xF0;
104 dest[1] = ((ch>>12) & 0x3F) | 0x80;
105 dest[2] = ((ch>>6) & 0x3F) | 0x80;
106 dest[3] = (ch & 0x3F) | 0x80;
107 return 4;
108 }
109 return 0;
110 }
111
112 LtkTextManager *
113 ltk_init_text(char *font_name)
114 {
115 LtkTextManager *tm = malloc(sizeof(LtkTextManager));
116 if (!tm) {
117 (void)printf("Memory exhausted when trying to create text manager.");
118 exit(1);
119 }
120 tm->font_paths = kh_init(fontid);
121 tm->font_cache = kh_init(fontstruct);
122 tm->glyph_cache = kh_init(glyphcache);
123 /* FIXME: THIS REALLY SHOULD NOT BE UINT16_T! IT GETS MESSY WITH BIT-SHIFTING */
124 tm->font_id_cur = 1;
125 ltk_load_default_font(tm, font_name);
126
127 return tm;
128 }
129
130 void
131 ltk_destroy_text_manager(LtkTextManager *tm)
132 {
133 int k;
134
135 kh_destroy(fontid, tm->font_paths);
136
137 for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) {
138 if (kh_exist(tm->font_cache, k)) {
139 ltk_destroy_font(kh_value(tm->font_cache, k));
140 }
141 }
142 kh_destroy(fontstruct, tm->font_cache);
143
144 for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
145 if (kh_exist(tm->glyph_cache, k)) {
146 ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
147 }
148 }
149 kh_destroy(glyphcache, tm->glyph_cache);
150
151 free(tm);
152 }
153
154 LtkGlyphInfo *
155 ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
156 {
157 LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
158 if (!glyph) {
159 (void)printf("Out of memory!\n");
160 exit(1);
161 }
162
163 glyph->id = id;
164 glyph->refs = 0;
165 glyph->alphamap = stbtt_GetGlyphBitmap(
166 &font->info, scale, scale, id, &glyph->w,
167 &glyph->h, &glyph->xoff, &glyph->yoff
168 );
169
170 return glyph;
171 }
172
173 void
174 ltk_destroy_glyph_info(LtkGlyphInfo *gi)
175 {
176 free(gi->alphamap);
177 free(gi);
178 }
179
180 LtkGlyphInfo *
181 ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
182 {
183 int ret;
184 khint_t k;
185 LtkGlyphInfo *glyph;
186 k = kh_get(glyphinfo, cache, id);
187 if (k == kh_end(cache)) {
188 glyph = ltk_create_glyph_info(font, id, scale);
189 /* FIXME: error checking with ret */
190 k = kh_put(glyphinfo, cache, id, &ret);
191 kh_value(cache, k) = glyph;
192 } else {
193 glyph = kh_value(cache, k);
194 }
195
196 return glyph;
197 }
198
199 khint_t
200 ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
201 {
202 khash_t(glyphinfo) *cache = kh_init(glyphinfo);
203 int ret;
204 khint_t k;
205 /* I guess I can just ignore ret for now */
206 k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
207 kh_value(tm->glyph_cache, k) = cache;
208
209 return k;
210 }
211
212 void
213 ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
214 {
215 int k;
216 for (k = kh_begin(cache); k != kh_end(cache); k++) {
217 if (kh_exist(cache, k)) {
218 ltk_destroy_glyph_info(kh_value(cache, k));
219 }
220 }
221 kh_destroy(glyphinfo, cache);
222 }
223
224 void
225 ltk_load_default_font(LtkTextManager *tm, char *name)
226 {
227 FcPattern *match;
228 FcResult result;
229 char *file;
230 int index;
231 uint16_t font;
232
233 tm->fcpattern = FcNameParse(name);
234 FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype");
235 FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern);
236 FcDefaultSubstitute(tm->fcpattern);
237 match = FcFontMatch(NULL, tm->fcpattern, &result);
238
239 FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file);
240 /* FIXME: Why is index never used? This is the index within the font file,
241 so it might be important, although I'm not sure if stb_truetype even
242 supports it */
243 FcPatternGetInteger (match, FC_INDEX, 0, &index);
244
245 tm->default_font = ltk_get_font(tm, file);
246
247 FcPatternDestroy (match);
248 }
249
250 LtkFont *
251 ltk_create_font(char *path, uint16_t id)
252 {
253 long len;
254 LtkFont *font = malloc(sizeof(LtkFont));
255 if (!font) {
256 (void)fprintf(stderr, "Out of memory!\n");
257 exit(1);
258 }
259 char *contents = ltk_read_file(path, &len);
260 if (!stbtt_InitFont(&font->info, contents, 0))
261 {
262 (void)fprintf(stderr, "Failed to load font %s\n", path);
263 exit(1);
264 }
265 /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
266 hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
267 hb_face_t *face = hb_face_create(blob, 0);
268 /* FIXME: need to use destroy function in order for the original file data to be freed? */
269 hb_blob_destroy(blob);
270 font->hb = hb_font_create(face);
271 hb_face_destroy(face);
272 hb_ot_font_set_funcs(font->hb);
273 font->id = id;
274 font->refs = 0;
275
276 return font;
277 }
278
279 void
280 ltk_destroy_font(LtkFont *font)
281 {
282 free(font->info.data);
283 hb_font_destroy(font->hb);
284 free(font);
285 }
286
287 uint16_t
288 ltk_load_font(LtkTextManager *tm, char *path)
289 {
290 LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
291 int ret;
292 khint_t k;
293 /* FIXME: does kh_destroy also free these copied strings properly? */
294 char *key = strdup(path);
295 k = kh_put(fontid, tm->font_paths, key, &ret);
296 kh_value(tm->font_paths, k) = font->id;
297 k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
298 kh_value(tm->font_cache, k) = font;
299 k = kh_get(fontid, tm->font_paths, path);
300
301 return font->id;
302 }
303
304 uint16_t
305 ltk_get_font(LtkTextManager *tm, char *path)
306 {
307 int ret;
308 khint_t k;
309 uint16_t id;
310 k = kh_get(fontid, tm->font_paths, path);
311 if (k == kh_end(tm->font_paths)) {
312 id = ltk_load_font(tm, path);
313 } else {
314 id = kh_value(tm->font_paths, k);
315 }
316
317 return id;
318 }
319
320 void
321 ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache)
322 {
323 int k;
324 if (--glyph->info->refs < 1) {
325 k = kh_get(glyphinfo, cache, glyph->info->id);
326 kh_del(glyphinfo, cache, k);
327 ltk_destroy_glyph_info(glyph->info);
328 }
329 free(glyph);
330 }
331
332 #if 0
333 /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
334 XImage *
335 ltk_render_text_line(
336 LtkTextLine *tl,
337 Display *dpy,
338 Window window,
339 GC gc,
340 Colormap colormap,
341 XColor fg,
342 XColor bg)
343 {
344 XWindowAttributes attrs;
345 XGetWindowAttributes(dpy, window, &attrs);
346 int depth = attrs.depth;
347 XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, tl->w, tl->h, 32, 0);
348 img->data = calloc(img->bytes_per_line, img->height);
349 XInitImage(img);
350 int b;
351 for (int i = 0; i < tl->h; i++) {
352 b = img->bytes_per_line * i;
353 for (int j = 0; j < tl->w; j++) {
354 img->data[b++] = bg.blue / 257;
355 img->data[b++] = bg.green / 257;
356 img->data[b++] = bg.red / 257;
357 b++;
358 }
359 }
360
361 LtkTextSegment *ts = tl->start_segment;
362 int x = 0;
363 int y = 0;
364 int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir);
365 do {
366 if (is_hor) {
367 y = tl->h - tl->y_max;
368 ltk_render_text_segment(ts, x + ts->start_x, y, img, fg);
369 x += ts->w;
370 } else {
371 x = tl->w - tl->x_max;
372 ltk_render_text_segment(ts, x, y + ts->start_y, img, fg);
373 y += ts->h;
374 }
375 } while (ts = ts->next);
376
377 return img;
378 }
379
380 void
381 ltk_render_text_segment(
382 LtkTextSegment *ts,
383 unsigned int start_x,
384 unsigned int start_y,
385 XImage *img,
386 XColor fg)
387 {
388 LtkGlyph *glyph = ts->start_glyph;
389 int x_cur = start_x;
390 int y_cur = start_y;
391 int x, y;
392 double a;
393 int b;
394 do {
395 x = x_cur + glyph->info->xoff + glyph->x_offset;
396 y = y_cur + glyph->info->yoff - glyph->y_offset;
397 for (int i = 0; i < glyph->info->h; i++) {
398 for (int j = 0; j < glyph->info->w; j++) {
399 b = (y + i) * img->bytes_per_line + (x + j) * 4;
400 a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
401 img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257;
402 img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257;
403 img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257;
404 }
405 }
406 x_cur += glyph->x_advance;
407 y_cur -= glyph->y_advance;
408 } while (glyph = glyph->next);
409 }
410 #endif