ttext_stb.c - ltk - Socket-based GUI for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
ttext_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 }