URI:
       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 }