text-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