text_stb.c (19878B)
1 /* FIXME: max cache size for glyphs */ 2 /* 3 * Copyright (c) 2017, 2018, 2020, 2022 lumidify <nobody@lumidify.org> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <stdint.h> 21 #include <limits.h> 22 #include <stdarg.h> 23 24 #include <X11/Xlib.h> 25 #include <X11/Xutil.h> 26 27 #include <fontconfig/fontconfig.h> 28 29 #include "khash.h" 30 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */ 31 32 #include "xlib_shared.h" 33 #include "memory.h" 34 #include "color.h" 35 #include "rect.h" 36 #include "widget.h" 37 #include "ltk.h" 38 #include "util.h" 39 #include "text.h" 40 41 /* FIXME: Actually implement reference counting for the glyphs! */ 42 43 typedef struct { 44 stbtt_fontinfo info; 45 char *path; 46 int index; /* index in font file */ 47 uint16_t id; 48 unsigned int refs; 49 } ltk_font; 50 51 /* Contains general info on glyphs that doesn't change regardless of the context */ 52 typedef struct { 53 int id; /* FIXME: shouldn't this be uint32_t or larger? */ 54 unsigned char *alphamap; 55 int w; 56 int h; 57 int xoff; /* x offset from origin to top left corner of glyph */ 58 int yoff; /* y offset from origin to top left corner of glyph */ 59 unsigned int refs; 60 /* FIXME: does refs need to be long? It could cause problems if a 61 program tries to cache/"keep alive" a lot of pages of text. */ 62 } ltk_glyph_info; 63 64 /* Contains glyph info specific to one run of text */ 65 typedef struct { 66 ltk_glyph_info *info; 67 uint32_t codepoint; 68 int x; 69 int y; 70 } ltk_glyph; 71 72 struct ltk_text_line { 73 ltk_text_context *ctx; 74 char *text; 75 ltk_glyph *glyphs; 76 size_t glyph_len; 77 78 int *line_indeces; 79 int lines; 80 int lines_alloc; 81 82 int w_max; 83 int w; 84 int h; 85 int line_h; 86 int x_min; 87 int y_min; 88 int dirty; 89 uint16_t font_size; 90 }; 91 92 /* Hash definitions */ 93 /* glyph id -> glyph info struct */ 94 KHASH_MAP_INIT_INT(glyphinfo, ltk_glyph_info*) 95 /* font path, size -> glyph cache hash */ 96 KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*) 97 98 struct ltk_text_context { 99 ltk_renderdata *data; 100 khash_t(glyphcache) *glyph_cache; 101 ltk_font **fonts; 102 int num_fonts; 103 int fonts_bufsize; 104 ltk_font *default_font; 105 uint16_t font_id_cur; 106 }; 107 108 static ltk_font *ltk_get_font(ltk_text_context *ctx, char *path, int index); 109 static ltk_glyph_info *ltk_create_glyph_info(ltk_font *font, int id, 110 float scale); 111 static void ltk_destroy_glyph_info(ltk_glyph_info *gi); 112 static ltk_glyph_info *ltk_get_glyph_info(ltk_font *font, int id, 113 float scale, khash_t(glyphinfo) *cache); 114 static khash_t(glyphinfo) *ltk_get_glyph_cache(ltk_text_context *ctx, uint16_t font_id, 115 uint16_t font_size); 116 static khint_t ltk_create_glyph_cache(ltk_text_context *ctx, uint16_t font_id, uint16_t font_size); 117 static void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache); 118 static ltk_font *ltk_create_font(char *path, uint16_t id, int index); 119 static void ltk_destroy_font(ltk_font *font); 120 static ltk_font *ltk_load_font(ltk_text_context *ctx, char *path, int index); 121 static void ltk_load_default_font(ltk_text_context *ctx, char *default_font); 122 static void ltk_text_to_glyphs(ltk_text_context *ctx, ltk_glyph *glyphs, int num_glyphs, char *text, 123 uint16_t font_size, int *x_min, int *y_min, int *x_max, int *y_max); 124 static void ltk_text_line_create_glyphs(ltk_text_line *tl); 125 static void ltk_text_line_draw_glyph(ltk_glyph *glyph, int x, int y, 126 XImage *img, XColor fg); 127 /*static XImage *ltk_create_ximage(int w, int h, int depth, XColor bg);*/ 128 129 130 /* These unicode routines are taken from 131 * https://github.com/JeffBezanson/cutef8 */ 132 133 /* static size_t u8_wc_toutf8(char *dest, uint32_t ch); */ 134 static size_t u8_strlen(const char *s); 135 static uint32_t u8_nextmemchar(const char *s, size_t *i); 136 137 /* is c the start of a utf8 sequence? */ 138 #define isutf(c) (((c)&0xC0)!=0x80) 139 140 static const uint32_t offsetsFromUTF8[6] = { 141 0x00000000UL, 0x00003080UL, 0x000E2080UL, 142 0x03C82080UL, 0xFA082080UL, 0x82082080UL 143 }; 144 145 /* next character without NUL character terminator */ 146 static uint32_t u8_nextmemchar(const char *s, size_t *i) { 147 uint32_t ch = 0; 148 size_t sz = 0; 149 do { 150 ch <<= 6; 151 ch += (unsigned char)s[(*i)++]; 152 sz++; 153 } while (!isutf(s[*i])); 154 ch -= offsetsFromUTF8[sz-1]; 155 156 return ch; 157 } 158 159 /* number of characters in NUL-terminated string */ 160 static size_t u8_strlen(const char *s) { 161 size_t count = 0; 162 size_t i = 0, lasti; 163 164 while (1) { 165 lasti = i; 166 while (s[i] > 0) 167 i++; 168 count += (i-lasti); 169 if (s[i++]==0) break; 170 (void)(isutf(s[++i]) || isutf(s[++i]) || ++i); 171 count++; 172 } 173 return count; 174 } 175 176 /* 177 static size_t u8_wc_toutf8(char *dest, uint32_t ch) { 178 if (ch < 0x80) { 179 dest[0] = (char)ch; 180 return 1; 181 } 182 if (ch < 0x800) { 183 dest[0] = (ch>>6) | 0xC0; 184 dest[1] = (ch & 0x3F) | 0x80; 185 return 2; 186 } 187 if (ch < 0x10000) { 188 dest[0] = (ch>>12) | 0xE0; 189 dest[1] = ((ch>>6) & 0x3F) | 0x80; 190 dest[2] = (ch & 0x3F) | 0x80; 191 return 3; 192 } 193 if (ch < 0x110000) { 194 dest[0] = (ch>>18) | 0xF0; 195 dest[1] = ((ch>>12) & 0x3F) | 0x80; 196 dest[2] = ((ch>>6) & 0x3F) | 0x80; 197 dest[3] = (ch & 0x3F) | 0x80; 198 return 4; 199 } 200 return 0; 201 } 202 */ 203 204 ltk_text_context * 205 ltk_text_context_create(ltk_renderdata *data, char *default_font) { 206 ltk_text_context *ctx = ltk_malloc(sizeof(ltk_text_context)); 207 ctx->data = data; 208 ctx->glyph_cache = kh_init(glyphcache); 209 ctx->fonts = ltk_malloc(sizeof(ltk_font *)); 210 ctx->num_fonts = 0; 211 ctx->fonts_bufsize = 1; 212 ltk_load_default_font(ctx, default_font); 213 ctx->font_id_cur = 1; 214 return ctx; 215 } 216 217 void 218 ltk_text_context_destroy(ltk_text_context *ctx) { 219 for (int i = 0; i < ctx->num_fonts; i++) { 220 ltk_destroy_font(ctx->fonts[i]); 221 } 222 ltk_free(ctx->fonts); 223 if (!ctx->glyph_cache) return; 224 for (khint_t k = kh_begin(ctx->glyph_cache); k != kh_end(ctx->glyph_cache); k++) { 225 if (kh_exist(ctx->glyph_cache, k)) { 226 ltk_destroy_glyph_cache(kh_value(ctx->glyph_cache, k)); 227 } 228 } 229 kh_destroy(glyphcache, ctx->glyph_cache); 230 ltk_free(ctx); 231 /* FIXME: figure out where this should go */ 232 FcFini(); 233 } 234 235 static ltk_glyph_info * 236 ltk_create_glyph_info(ltk_font *font, int id, float scale) { 237 ltk_glyph_info *glyph = ltk_malloc(sizeof(ltk_glyph_info)); 238 239 glyph->id = id; 240 glyph->refs = 0; 241 glyph->alphamap = stbtt_GetGlyphBitmap( 242 &font->info, scale, scale, id, &glyph->w, 243 &glyph->h, &glyph->xoff, &glyph->yoff 244 ); 245 246 return glyph; 247 } 248 249 static void 250 ltk_destroy_glyph_info(ltk_glyph_info *gi) { 251 ltk_free(gi->alphamap); 252 ltk_free(gi); 253 } 254 255 static ltk_glyph_info * 256 ltk_get_glyph_info(ltk_font *font, int id, float scale, khash_t(glyphinfo) *cache) { 257 int ret; 258 khint_t k; 259 ltk_glyph_info *glyph; 260 k = kh_get(glyphinfo, cache, id); 261 if (k == kh_end(cache)) { 262 glyph = ltk_create_glyph_info(font, id, scale); 263 /* FIXME: error checking with ret */ 264 k = kh_put(glyphinfo, cache, id, &ret); 265 kh_value(cache, k) = glyph; 266 } else { 267 glyph = kh_value(cache, k); 268 } 269 270 return glyph; 271 } 272 273 static khash_t(glyphinfo) * 274 ltk_get_glyph_cache(ltk_text_context *ctx, uint16_t font_id, uint16_t font_size) { 275 khint_t k; 276 uint32_t attr = ((uint32_t)font_id << 16) + font_size; 277 k = kh_get(glyphcache, ctx->glyph_cache, attr); 278 if (k == kh_end(ctx->glyph_cache)) { 279 k = ltk_create_glyph_cache(ctx, font_id, font_size); 280 } 281 return kh_value(ctx->glyph_cache, k); 282 } 283 284 static khint_t 285 ltk_create_glyph_cache(ltk_text_context *ctx, uint16_t font_id, uint16_t font_size) { 286 khash_t(glyphinfo) *cache = kh_init(glyphinfo); 287 int ret; 288 khint_t k; 289 /* I guess I can just ignore ret for now */ 290 k = kh_put(glyphcache, ctx->glyph_cache, ((uint32_t)font_id << 16) + font_size, &ret); 291 kh_value(ctx->glyph_cache, k) = cache; 292 293 return k; 294 } 295 296 static void 297 ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache) { 298 for (khint_t k = kh_begin(cache); k != kh_end(cache); k++) { 299 if (kh_exist(cache, k)) { 300 ltk_destroy_glyph_info(kh_value(cache, k)); 301 } 302 } 303 kh_destroy(glyphinfo, cache); 304 } 305 306 static void 307 ltk_load_default_font(ltk_text_context *ctx, char *name) { 308 FcPattern *match, *pat; 309 FcResult result; 310 char *file; 311 int index; 312 313 /* FIXME: Get rid of this stupid cast somehow */ 314 pat = FcNameParse((const FcChar8 *)name); 315 FcPatternAddString(pat, FC_FONTFORMAT, (const FcChar8 *)"truetype"); 316 FcConfigSubstitute(NULL, pat, FcMatchPattern); 317 FcDefaultSubstitute(pat); 318 /* FIXME look at result */ 319 match = FcFontMatch(NULL, pat, &result); 320 321 FcPatternGetString(match, FC_FILE, 0, (FcChar8 **) &file); 322 FcPatternGetInteger(match, FC_INDEX, 0, &index); 323 324 ctx->default_font = ltk_get_font(ctx, file, index); 325 326 FcPatternDestroy(match); 327 FcPatternDestroy(pat); 328 } 329 330 static ltk_font * 331 ltk_create_font(char *path, uint16_t id, int index) { 332 unsigned long len; 333 ltk_font *font = ltk_malloc(sizeof(ltk_font)); 334 char *errstr = NULL; 335 char *contents = ltk_read_file(path, &len, &errstr); 336 if (!contents) 337 ltk_fatal_errno("Unable to read font file %s: %s\n", path, errstr); 338 int offset = stbtt_GetFontOffsetForIndex((unsigned char *)contents, index); 339 font->info.data = NULL; 340 if (!stbtt_InitFont(&font->info, (unsigned char *)contents, offset)) 341 ltk_fatal("Failed to load font %s\n", path); 342 font->id = id; 343 font->refs = 0; 344 font->index = index; 345 font->path = ltk_strdup(path); 346 347 return font; 348 } 349 350 static void 351 ltk_destroy_font(ltk_font *font) { 352 ltk_free(font->path); 353 ltk_free(font->info.data); 354 ltk_free(font); 355 } 356 357 static ltk_font * 358 ltk_load_font(ltk_text_context *ctx, char *path, int index) { 359 ltk_font *font = ltk_create_font(path, ctx->font_id_cur++, index); 360 if (ctx->num_fonts == ctx->fonts_bufsize) { 361 ltk_font **new = ltk_realloc(ctx->fonts, ctx->fonts_bufsize * 2 * sizeof(ltk_font *)); 362 ctx->fonts = new; 363 ctx->fonts_bufsize *= 2; 364 } 365 ctx->fonts[ctx->num_fonts] = font; 366 ctx->num_fonts++; 367 return font; 368 } 369 370 static ltk_font * 371 ltk_get_font(ltk_text_context *ctx, char *path, int index) { 372 ltk_font *font = NULL; 373 for (int i = 0; i < ctx->num_fonts; i++) { 374 if (ctx->fonts[i]->index == index && 375 strcmp(ctx->fonts[i]->path, path) == 0) { 376 font = ctx->fonts[i]; 377 break; 378 } 379 } 380 if (!font) 381 font = ltk_load_font(ctx, path, index); 382 return font; 383 } 384 385 static void 386 ltk_text_to_glyphs(ltk_text_context *ctx, ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_size, 387 int *x_min, int *y_min, int *x_max, int *y_max) { 388 uint32_t c1, c2 = 0; 389 int gid; 390 int index; 391 FcChar8 *file; 392 size_t inc = 0; 393 int x = 0, y, kern_advance, ax; 394 int x1_abs, x2_abs; 395 float scale; 396 int ascent, descent, line_gap; 397 *x_min = INT_MAX, *x_max = INT_MIN, *y_min = INT_MAX, *y_max = INT_MIN; 398 ltk_glyph_info *ginfo; 399 400 ltk_font *font = ctx->default_font; 401 khash_t(glyphinfo) *glyph_cache = ltk_get_glyph_cache(ctx, font->id, font_size); 402 403 scale = stbtt_ScaleForPixelHeight(&font->info, font_size); 404 stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap); 405 ascent *= scale; 406 descent *= scale; 407 408 c1 = u8_nextmemchar(text, &inc); 409 for (int i = 0; i < num_glyphs; i++) { 410 gid = stbtt_FindGlyphIndex(&font->info, c1); 411 if (!gid) { 412 /* Question: Why does this not work with FcPatternDuplicate? */ 413 FcPattern *pat = FcPatternCreate(); 414 FcPattern *match; 415 /* FIXME: use result */ 416 FcResult result; 417 FcPatternAddBool(pat, FC_SCALABLE, 1); 418 FcConfigSubstitute(NULL, pat, FcMatchPattern); 419 FcDefaultSubstitute(pat); 420 FcCharSet *cs = FcCharSetCreate(); 421 FcCharSetAddChar(cs, c1); 422 FcPatternAddCharSet(pat, FC_CHARSET, cs); 423 match = FcFontMatch(NULL, pat, &result); 424 FcPatternGetString(match, FC_FILE, 0, &file); 425 FcPatternGetInteger(match, FC_INDEX, 0, &index); 426 font = ltk_get_font(ctx, (char *)file, index); 427 glyph_cache = ltk_get_glyph_cache(ctx, font->id, font_size); 428 FcPatternDestroy(match); 429 FcPatternDestroy(pat); 430 gid = stbtt_FindGlyphIndex(&font->info, c1); 431 scale = stbtt_ScaleForPixelHeight(&font->info, font_size); 432 stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap); 433 ascent *= scale; 434 descent *= scale; 435 } 436 ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache); 437 ginfo->refs++; 438 y = ascent + ginfo->yoff; 439 x1_abs = x + ginfo->xoff; 440 441 glyphs[i].x = x1_abs; 442 glyphs[i].y = y; 443 glyphs[i].codepoint = c1; 444 445 stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0); 446 x += (int) (ax * scale); 447 x2_abs = x; 448 449 glyphs[i].info = ginfo; 450 if (x1_abs < *x_min) *x_min = x1_abs; 451 if (y < *y_min) *y_min = y; 452 if (x2_abs > *x_max) *x_max = x2_abs; 453 if (y + ginfo->h > *y_max) *y_max = y + ginfo->h; 454 455 if (i != num_glyphs - 1) { 456 c2 = u8_nextmemchar(text, &inc); 457 kern_advance = stbtt_GetCodepointKernAdvance(&font->info, c1, c2); 458 x += (int) (kern_advance * scale); 459 } 460 c1 = c2; 461 } 462 } 463 464 /* 465 void 466 ltk_unref_glyph(ltk_glyph *glyph, khash_t(glyphinfo) *cache) { 467 int k; 468 if (--glyph->info->refs < 1) { 469 k = kh_get(glyphinfo, cache, glyph->info->id); 470 kh_del(glyphinfo, cache, k); 471 ltk_destroy_glyph_info(glyph->info); 472 } 473 } 474 475 void 476 ltk_unref_glyphs(ltk_glyph *glyphs, int num_glyphs) { 477 for (int i = 0; i < num_glyphs; i++) 478 ltk_unref_glyph(&glyphs[i]); 479 } 480 */ 481 482 /* 483 static void 484 ltk_fill_ximage(XImage *img, int w, int h, XColor bg) { 485 int b; 486 for (int i = 0; i < h; i++) { 487 b = img->bytes_per_line * i; 488 for (int j = 0; j < w; j++) { 489 img->data[b++] = bg.blue / 257; 490 img->data[b++] = bg.green / 257; 491 img->data[b++] = bg.red / 257; 492 b++; 493 } 494 } 495 } 496 497 static XImage * 498 ltk_create_ximage(int w, int h, int depth, XColor bg) { 499 XImage *img = XCreateImage(tm.dpy, CopyFromParent, depth, ZPixmap, 0, NULL, w, h, 32, 0); 500 img->data = ltk_calloc(img->bytes_per_line, img->height); 501 XInitImage(img); 502 ltk_fill_ximage(img, w, h, bg); 503 504 return img; 505 } 506 */ 507 508 /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */ 509 /* FIXME: make this work with other pixel representations */ 510 /* FIXME: will fail horribly if depth is not 32 */ 511 static void 512 ltk_text_line_draw_glyph(ltk_glyph *glyph, int x, int y, XImage *img, XColor fg) { 513 double a; 514 int b; 515 for (int i = 0; i < glyph->info->h; i++) { 516 for (int j = 0; j < glyph->info->w; j++) { 517 /* FIXME: this check could be moved to the for loop condition and initialization */ 518 /* -> not sure it that would *possibly* be a tiny bit faster */ 519 if (y + i >= img->height || x + j >= img->width || 520 y + i < 0 || x + i < 0) 521 continue; 522 b = (y + i) * img->bytes_per_line + (x + j) * 4; 523 a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0; 524 img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257; 525 img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257; 526 img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257; 527 } 528 } 529 } 530 531 static void 532 ltk_text_line_break_lines(ltk_text_line *tl) { 533 tl->lines = 1; 534 if (tl->w_max == -1) 535 return; 536 537 if (!tl->line_indeces) { 538 tl->line_indeces = ltk_malloc(sizeof(int)); 539 tl->lines_alloc = 1; 540 } 541 tl->w = tl->w_max; 542 543 /* FIXME: make this actually work properly */ 544 size_t last_space = 0; 545 size_t last_break = 0; 546 int last_break_pos = tl->x_min; 547 size_t i = 0; 548 while (i < tl->glyph_len) { 549 /* 0x20 == space character */ 550 if (tl->glyphs[i].codepoint == 0x20) 551 last_space = i + 1; /* actually break after the space */ 552 if (tl->glyphs[i].x + tl->glyphs[i].info->w - last_break_pos > tl->w) { 553 /* FIXME: safeguard for widths smaller than a single char */ 554 if (last_space > last_break) 555 last_break = last_space; 556 else 557 last_break = i; 558 i = last_break - 1; 559 last_break_pos = tl->glyphs[last_break].x; 560 if (tl->lines >= tl->lines_alloc) { 561 tl->lines_alloc = tl->lines_alloc ? tl->lines_alloc * 2 : 2; 562 tl->line_indeces = ltk_realloc(tl->line_indeces, tl->lines_alloc * sizeof(int)); 563 } 564 tl->line_indeces[tl->lines - 1] = last_break; 565 tl->lines++; 566 } 567 i++; 568 } 569 tl->h = tl->line_h * tl->lines; 570 tl->dirty = 0; 571 } 572 573 /* FIXME: improve color handling - where passed as pointer, where as value? 574 In certain cases, it's important to deallocate the color in the end 575 (if the x server doesn't support truecolor - I'm not sure right now if 576 this is the right terminology, but it's something like that) */ 577 578 void 579 ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y) { 580 if (tl->dirty) 581 ltk_text_line_break_lines(tl); 582 int xoff = 0, yoff = 0; 583 if (x < 0) { 584 xoff = x; 585 x = 0; 586 } 587 if (y < 0) { 588 yoff = y; 589 y = 0; 590 } 591 int s_w, s_h; 592 ltk_surface_get_size(s, &s_w, &s_h); 593 int w = x + xoff + tl->w > s_w ? s_w - x : xoff + tl->w; 594 int h = y + yoff + tl->h > s_h ? s_h - y : yoff + tl->h; 595 if (w <= 0 || h <= 0) 596 return; 597 Drawable d = ltk_surface_get_drawable(s); 598 XImage *img = XGetImage(tl->ctx->data->dpy, d, x, y, w, h, 0xFFFFFF, ZPixmap); 599 600 int last_break = 0; 601 for (int i = 0; i < tl->lines; i++) { 602 int next_break; 603 if (i <= tl->lines - 2) 604 next_break = tl->line_indeces[i]; 605 else 606 next_break = tl->glyph_len; 607 for (int j = last_break; j < next_break; j++) { 608 int g_x = tl->glyphs[j].x - tl->glyphs[last_break].x + xoff; 609 int g_y = tl->glyphs[j].y - tl->y_min + tl->line_h * i + yoff; 610 ltk_text_line_draw_glyph(&tl->glyphs[j], g_x, g_y, img, color->xcolor); 611 } 612 last_break = next_break; 613 } 614 XPutImage(tl->ctx->data->dpy, d, tl->ctx->data->gc, img, 0, 0, x, y, w, h); 615 XDestroyImage(img); 616 } 617 618 /* FIXME: ACTUALLY IMPLEMENT!!! */ 619 void 620 ltk_text_line_draw_clipped(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y, ltk_rect clip) { 621 (void)clip; 622 ltk_text_line_draw(tl, s, color, x, y); 623 } 624 625 ltk_rect 626 ltk_text_line_get_minimal_clip_rect(ltk_text_line *tl, ltk_rect clip) { 627 (void)clip; 628 if (tl->dirty) 629 ltk_text_line_break_lines(tl); 630 return (ltk_rect){0, 0, tl->w, tl->h}; 631 } 632 633 void 634 ltk_text_line_set_width(ltk_text_line *tl, int width) { 635 /* FIXME: clarify what the difference between w_max and w is */ 636 /* FIXME: tl->w could be made slightly less than tl->w_max depending on breaking */ 637 tl->w_max = width; 638 tl->w = width; 639 tl->dirty = 1; 640 } 641 642 void 643 ltk_text_line_get_size(ltk_text_line *tl, int *w, int *h) { 644 if (tl->dirty) 645 ltk_text_line_break_lines(tl); 646 *w = tl->w; 647 *h = tl->h; 648 } 649 650 static void 651 ltk_text_line_create_glyphs(ltk_text_line *tl) { 652 int x_min, x_max, y_min, y_max; 653 ltk_text_to_glyphs(tl->ctx, tl->glyphs, tl->glyph_len, tl->text, tl->font_size, 654 &x_min, &y_min, &x_max, &y_max); 655 /* for drawing the glyphs at the right position on the image */ 656 tl->x_min = x_min; 657 tl->y_min = y_min; 658 tl->w = x_max - x_min; 659 tl->h = tl->line_h = y_max - y_min; 660 } 661 662 ltk_text_line * 663 ltk_text_line_create(ltk_text_context *ctx, uint16_t font_size, char *text, int take_over_text, int width) { 664 ltk_text_line *tl = ltk_malloc(sizeof(ltk_text_line)); 665 tl->ctx = ctx; 666 if (take_over_text) 667 tl->text = text; 668 else 669 tl->text = ltk_strdup(text); 670 tl->glyph_len = u8_strlen(text); 671 tl->glyphs = ltk_malloc(tl->glyph_len * sizeof(ltk_glyph)); 672 tl->font_size = font_size; 673 tl->w_max = width; 674 ltk_text_line_create_glyphs(tl); 675 tl->lines_alloc = tl->lines = 0; 676 tl->line_indeces = NULL; 677 tl->dirty = 1; 678 return tl; 679 } 680 681 void 682 ltk_text_line_destroy(ltk_text_line *tl) { 683 ltk_free(tl->text); 684 /* FIXME: Reference count glyph infos */ 685 ltk_free(tl->glyphs); 686 if (tl->line_indeces) 687 ltk_free(tl->line_indeces); 688 ltk_free(tl); 689 }