ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

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 }