ltkx

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltkx.git
Log | Files | Refs | README | LICENSE

commit f344c3f28da7995f19ec0549d90f275d3fd1715a
parent 57d85ccfe4c9ac3de63dab6979e65dbe0696f4b7
Author: lumidify <nobody@lumidify.org>
Date:   Fri,  8 May 2020 20:57:03 +0200

Move common text functions to text-common.*

Diffstat:
MMakefile | 4++--
Mbutton.c | 1+
Mgap_buffer.h | 4++--
Mltk.c | 1+
Mltkx.h | 1+
Atext-common.c | 409+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atext-common.h | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtext-hb.c | 291+------------------------------------------------------------------------------
Mtext-hb.h | 83++-----------------------------------------------------------------------------
9 files changed, 544 insertions(+), 375 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ LIBS = -lm `pkg-config --libs x11 harfbuzz fontconfig fribidi` STD = -std=c99 CFLAGS = -g -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 harfbuzz fontconfig fribidi` -pedantic -OBJ = stb_truetype.o text-hb.o ltk.o ini.o grid.o button.o test1.o +OBJ = stb_truetype.o text-common.o text-hb.o ltk.o ini.o grid.o button.o test1.o test1: $(OBJ) gcc $(STD) -o $@ $(OBJ) $(LIBS) @@ -12,4 +12,4 @@ test1: $(OBJ) .PHONY: clean clean: - rm -f stb_truetype.o text-hb.o ltk.o ini.o grid.o button.o test1.o test1 + rm -f stb_truetype.o text-common.o text-hb.o ltk.o ini.o grid.o button.o test1.o test1 diff --git a/button.c b/button.c @@ -29,6 +29,7 @@ #include <fribidi.h> #include <harfbuzz/hb.h> #include <fontconfig/fontconfig.h> +#include "text-common.h" #include "text-hb.h" #include "button.h" diff --git a/gap_buffer.h b/gap_buffer.h @@ -27,7 +27,7 @@ #include <stdio.h> #include <stdlib.h> -#define LTK_INIT_GAP_BUFFER_DECL(type) \ +#define LTK_GAP_BUFFER_INIT_DECL(type) \ struct ltk_gap_buffer_##type## { \ type *buf; \ size_t buf_size; \ @@ -47,7 +47,7 @@ void ltk_gap_buffer_move_gap_##type##( \ struct ltk_gap_buffer_##type## *gb, size_t pos); \ void ltk_gap_buffer_destroy_##type##(struct ltk_gap_buffer_##type## *gb); -#define LTK_INIT_GAP_BUFFER_IMPL(type) \ +#define LTK_GAP_BUFFER_INIT_IMPL(type) \ struct ltk_gap_buffer_##type## * \ ltk_gap_buffer_create_##type##(void) { \ struct ltk_gap_buffer_##type## *gb = \ diff --git a/ltk.c b/ltk.c @@ -32,6 +32,7 @@ #include <fribidi.h> #include <harfbuzz/hb.h> #include <fontconfig/fontconfig.h> +#include "text-common.h" #include "text-hb.h" Ltk *ltk_global; diff --git a/ltkx.h b/ltkx.h @@ -8,6 +8,7 @@ #include <fribidi.h> #include <harfbuzz/hb.h> #include <harfbuzz/hb-ot.h> +#include "text-common.h" #include "text-hb.h" #include "button.h" #include "grid.h" diff --git a/text-common.c b/text-common.c @@ -0,0 +1,409 @@ +/* + * This file is part of the Lumidify ToolKit (LTK) + * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */ +#include <fontconfig/fontconfig.h> +#include "khash.h" +#include <fribidi.h> +#include <harfbuzz/hb.h> +#include <harfbuzz/hb-ot.h> +#include "text-common.h" +#include "ltk.h" + +extern Ltk *ltk_global; + +/* These unicode routines are taken from + * https://github.com/JeffBezanson/cutef8 */ + +/* is c the start of a utf8 sequence? */ +#define isutf(c) (((c)&0xC0)!=0x80) + +static const uint32_t offsetsFromUTF8[6] = { + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + +/* next character without NUL character terminator */ +uint32_t u8_nextmemchar(const char *s, size_t *i) +{ + uint32_t ch = 0; + size_t sz = 0; + do { + ch <<= 6; + ch += (unsigned char)s[(*i)++]; + sz++; + } while (!isutf(s[*i])); + ch -= offsetsFromUTF8[sz-1]; + + return ch; +} + +/* number of characters in NUL-terminated string */ +size_t u8_strlen(const char *s) +{ + size_t count = 0; + size_t i = 0, lasti; + + while (1) { + lasti = i; + while (s[i] > 0) + i++; + count += (i-lasti); + if (s[i++]==0) break; + (void)(isutf(s[++i]) || isutf(s[++i]) || ++i); + count++; + } + return count; +} + +size_t u8_wc_toutf8(char *dest, uint32_t ch) +{ + if (ch < 0x80) { + dest[0] = (char)ch; + return 1; + } + if (ch < 0x800) { + dest[0] = (ch>>6) | 0xC0; + dest[1] = (ch & 0x3F) | 0x80; + return 2; + } + if (ch < 0x10000) { + dest[0] = (ch>>12) | 0xE0; + dest[1] = ((ch>>6) & 0x3F) | 0x80; + dest[2] = (ch & 0x3F) | 0x80; + return 3; + } + if (ch < 0x110000) { + dest[0] = (ch>>18) | 0xF0; + dest[1] = ((ch>>12) & 0x3F) | 0x80; + dest[2] = ((ch>>6) & 0x3F) | 0x80; + dest[3] = (ch & 0x3F) | 0x80; + return 4; + } + return 0; +} + +LtkTextManager * +ltk_init_text(char *font_name) +{ + LtkTextManager *tm = malloc(sizeof(LtkTextManager)); + if (!tm) { + (void)printf("Memory exhausted when trying to create text manager."); + exit(1); + } + tm->font_paths = kh_init(fontid); + tm->font_cache = kh_init(fontstruct); + tm->glyph_cache = kh_init(glyphcache); + tm->font_id_cur = 0; + ltk_load_default_font(tm, font_name); + + return tm; +} + +void +ltk_destroy_text_manager(LtkTextManager *tm) +{ + int k; + + kh_destroy(fontid, tm->font_paths); + + for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) { + if (kh_exist(tm->font_cache, k)) { + ltk_destroy_font(kh_value(tm->font_cache, k)); + } + } + kh_destroy(fontstruct, tm->font_cache); + + for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) { + if (kh_exist(tm->glyph_cache, k)) { + ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k)); + } + } + kh_destroy(glyphcache, tm->glyph_cache); + + free(tm); +} + +LtkGlyphInfo * +ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale) +{ + LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo)); + if (!glyph) { + (void)printf("Out of memory!\n"); + exit(1); + } + + glyph->id = id; + glyph->refs = 0; + glyph->alphamap = stbtt_GetGlyphBitmap( + &font->info, scale, scale, id, &glyph->w, + &glyph->h, &glyph->xoff, &glyph->yoff + ); + + return glyph; +} + +void +ltk_destroy_glyph_info(LtkGlyphInfo *gi) +{ + free(gi->alphamap); + free(gi); +} + +LtkGlyphInfo * +ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache) +{ + int ret; + khint_t k; + LtkGlyphInfo *glyph; + k = kh_get(glyphinfo, cache, id); + if (k == kh_end(cache)) { + glyph = ltk_create_glyph_info(font, id, scale); + /* FIXME: error checking with ret */ + k = kh_put(glyphinfo, cache, id, &ret); + kh_value(cache, k) = glyph; + } else { + glyph = kh_value(cache, k); + } + + return glyph; +} + +khint_t +ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size) +{ + khash_t(glyphinfo) *cache = kh_init(glyphinfo); + int ret; + khint_t k; + /* I guess I can just ignore ret for now */ + k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret); + kh_value(tm->glyph_cache, k) = cache; + + return k; +} + +void +ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache) +{ + int k; + for (k = kh_begin(cache); k != kh_end(cache); k++) { + if (kh_exist(cache, k)) { + ltk_destroy_glyph_info(kh_value(cache, k)); + } + } + kh_destroy(glyphinfo, cache); +} + +void +ltk_load_default_font(LtkTextManager *tm, char *name) +{ + FcPattern *match; + FcResult result; + char *file; + int index; + uint16_t font; + + tm->fcpattern = FcNameParse(name); + FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype"); + FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern); + FcDefaultSubstitute(tm->fcpattern); + match = FcFontMatch(NULL, tm->fcpattern, &result); + + FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file); + /* FIXME: Why is index never used? This is the index within the font file, + so it might be important, although I'm not sure if stb_truetype even + supports it */ + FcPatternGetInteger (match, FC_INDEX, 0, &index); + + tm->default_font = ltk_get_font(tm, file); + + FcPatternDestroy (match); +} + +LtkFont * +ltk_create_font(char *path, uint16_t id) +{ + long len; + LtkFont *font = malloc(sizeof(LtkFont)); + if (!font) { + (void)fprintf(stderr, "Out of memory!\n"); + exit(1); + } + char *contents = ltk_read_file(path, &len); + if (!stbtt_InitFont(&font->info, contents, 0)) + { + (void)fprintf(stderr, "Failed to load font %s\n", path); + exit(1); + } + /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */ + hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL); + hb_face_t *face = hb_face_create(blob, 0); + /* FIXME: need to use destroy function in order for the original file data to be freed? */ + hb_blob_destroy(blob); + font->hb = hb_font_create(face); + hb_face_destroy(face); + hb_ot_font_set_funcs(font->hb); + font->id = id; + font->refs = 0; + + return font; +} + +void +ltk_destroy_font(LtkFont *font) +{ + free(font->info.data); + hb_font_destroy(font->hb); + free(font); +} + +uint16_t +ltk_load_font(LtkTextManager *tm, char *path) +{ + LtkFont *font = ltk_create_font(path, tm->font_id_cur++); + int ret; + khint_t k; + /* FIXME: does kh_destroy also free these copied strings properly? */ + char *key = strdup(path); + k = kh_put(fontid, tm->font_paths, key, &ret); + kh_value(tm->font_paths, k) = font->id; + k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret); + kh_value(tm->font_cache, k) = font; + k = kh_get(fontid, tm->font_paths, path); + + return font->id; +} + +uint16_t +ltk_get_font(LtkTextManager *tm, char *path) +{ + int ret; + khint_t k; + uint16_t id; + k = kh_get(fontid, tm->font_paths, path); + if (k == kh_end(tm->font_paths)) { + id = ltk_load_font(tm, path); + } else { + id = kh_value(tm->font_paths, k); + } + + return id; +} + +void +ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache) +{ + int k; + if (--glyph->info->refs < 1) { + k = kh_get(glyphinfo, cache, glyph->info->id); + kh_del(glyphinfo, cache, k); + ltk_destroy_glyph_info(glyph->info); + } + free(glyph); +} + +#if 0 +/* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */ +XImage * +ltk_render_text_line( + LtkTextLine *tl, + Display *dpy, + Window window, + GC gc, + Colormap colormap, + XColor fg, + XColor bg) +{ + XWindowAttributes attrs; + XGetWindowAttributes(dpy, window, &attrs); + int depth = attrs.depth; + XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, tl->w, tl->h, 32, 0); + img->data = calloc(img->bytes_per_line, img->height); + XInitImage(img); + int b; + for (int i = 0; i < tl->h; i++) { + b = img->bytes_per_line * i; + for (int j = 0; j < tl->w; j++) { + img->data[b++] = bg.blue / 257; + img->data[b++] = bg.green / 257; + img->data[b++] = bg.red / 257; + b++; + } + } + + LtkTextSegment *ts = tl->start_segment; + int x = 0; + int y = 0; + int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir); + do { + if (is_hor) { + y = tl->h - tl->y_max; + ltk_render_text_segment(ts, x + ts->start_x, y, img, fg); + x += ts->w; + } else { + x = tl->w - tl->x_max; + ltk_render_text_segment(ts, x, y + ts->start_y, img, fg); + y += ts->h; + } + } while (ts = ts->next); + + return img; +} + +void +ltk_render_text_segment( + LtkTextSegment *ts, + unsigned int start_x, + unsigned int start_y, + XImage *img, + XColor fg) +{ + LtkGlyph *glyph = ts->start_glyph; + int x_cur = start_x; + int y_cur = start_y; + int x, y; + double a; + int b; + do { + x = x_cur + glyph->info->xoff + glyph->x_offset; + y = y_cur + glyph->info->yoff - glyph->y_offset; + for (int i = 0; i < glyph->info->h; i++) { + for (int j = 0; j < glyph->info->w; j++) { + b = (y + i) * img->bytes_per_line + (x + j) * 4; + a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0; + img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257; + img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257; + img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257; + } + } + x_cur += glyph->x_advance; + y_cur -= glyph->y_advance; + } while (glyph = glyph->next); +} +#endif diff --git a/text-common.h b/text-common.h @@ -0,0 +1,125 @@ +/* + * This file is part of the Lumidify ToolKit (LTK) + * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _TEXT_COMMON_H_ +#define _TEXT_COMMON_H_ + +/* +Requires the following includes: +<X11/Xlib.h>, <X11/Xutil.h>, "stb_truetype.h", +"khash.h", <harfbuzz/hb.h>, <fribidi.h>, +<fontconfig/fontconfig.h> +*/ + +typedef struct { + stbtt_fontinfo info; + hb_font_t *hb; + uint16_t id; + unsigned int refs; +} LtkFont; + +/* Contains general info on glyphs that doesn't change regardless of the context */ +typedef struct _LtkGlyphInfo { + unsigned int id; + unsigned char *alphamap; + unsigned int w; + unsigned int h; + unsigned int xoff; /* x offset from origin to top left corner of glyph */ + unsigned int yoff; /* y offset from origin to top left corner of glyph */ + unsigned int refs; + /* FIXME: does refs need to be long? It could cause problems if a + program tries to cache/"keep alive" a lot of pages of text. */ +} LtkGlyphInfo; + +/* Contains glyph info specific to one run of text */ +typedef struct _LtkGlyph { + LtkGlyphInfo *info; + int x_offset; /* additional x offset given by harfbuzz */ + int y_offset; /* additional y offset given by harfbuzz */ + int x_advance; + int y_advance; + int x_abs; + int y_abs; + uint32_t cluster; /* index of char in original text - from harfbuzz */ + struct _LtkGlyph *next; +} LtkGlyph; + +/* Hash definitions */ +/* glyph id -> glyph info struct */ +KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*) +/* font path, size -> glyph cache hash */ +KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*) +/* font path -> font id */ +KHASH_MAP_INIT_STR(fontid, uint16_t) +/* font id -> font struct */ +KHASH_MAP_INIT_INT(fontstruct, LtkFont*) + +typedef struct LtkTextManager { + khash_t(fontid) *font_paths; + khash_t(fontstruct) *font_cache; + khash_t(glyphcache) *glyph_cache; + FcPattern *fcpattern; + uint16_t default_font; + uint16_t font_id_cur; +} LtkTextManager; + +uint32_t u8_nextmemchar(const char *s, size_t *i); + +size_t u8_strlen(const char *s); + +size_t u8_wc_toutf8(char *dest, uint32_t ch); + +LtkTextManager *ltk_init_text(char *font_name); + +void ltk_destroy_text_manager(LtkTextManager *tm); + +LtkGlyphInfo *ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale); + +void ltk_destroy_glyph_info(LtkGlyphInfo *gi); + +LtkGlyphInfo *ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache); + +khint_t ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size); + +void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache); + +void ltk_load_default_font(LtkTextManager *tm, char *name); + +LtkFont *ltk_create_font(char *path, uint16_t id); + +void ltk_destroy_font(LtkFont *font); + +/* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */ +uint16_t ltk_load_font(LtkTextManager *tm, char *path); + +uint16_t ltk_get_font(LtkTextManager *tm, char *path); + +void ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache); + +/* +XImage *ltk_render_text_line(LtkTextLine *tl, Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg); + +void ltk_render_text_segment(LtkTextSegment *ts, unsigned int start_x, unsigned int start_y, XImage *img, XColor fg); +*/ + +#endif /* _TEXT_COMMON_H_ */ diff --git a/text-hb.c b/text-hb.c @@ -33,289 +33,12 @@ #include <fribidi.h> #include <harfbuzz/hb.h> #include <harfbuzz/hb-ot.h> +#include "text-common.h" #include "text-hb.h" #include "ltk.h" extern Ltk *ltk_global; -/* These unicode routines are taken from - * https://github.com/JeffBezanson/cutef8 */ - -/* is c the start of a utf8 sequence? */ -#define isutf(c) (((c)&0xC0)!=0x80) - -static const uint32_t offsetsFromUTF8[6] = { - 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL -}; - -/* next character without NUL character terminator */ -uint32_t u8_nextmemchar(const char *s, size_t *i) -{ - uint32_t ch = 0; - size_t sz = 0; - do { - ch <<= 6; - ch += (unsigned char)s[(*i)++]; - sz++; - } while (!isutf(s[*i])); - ch -= offsetsFromUTF8[sz-1]; - - return ch; -} - -/* number of characters in NUL-terminated string */ -size_t u8_strlen(const char *s) -{ - size_t count = 0; - size_t i = 0, lasti; - - while (1) { - lasti = i; - while (s[i] > 0) - i++; - count += (i-lasti); - if (s[i++]==0) break; - (void)(isutf(s[++i]) || isutf(s[++i]) || ++i); - count++; - } - return count; -} - -size_t u8_wc_toutf8(char *dest, uint32_t ch) -{ - if (ch < 0x80) { - dest[0] = (char)ch; - return 1; - } - if (ch < 0x800) { - dest[0] = (ch>>6) | 0xC0; - dest[1] = (ch & 0x3F) | 0x80; - return 2; - } - if (ch < 0x10000) { - dest[0] = (ch>>12) | 0xE0; - dest[1] = ((ch>>6) & 0x3F) | 0x80; - dest[2] = (ch & 0x3F) | 0x80; - return 3; - } - if (ch < 0x110000) { - dest[0] = (ch>>18) | 0xF0; - dest[1] = ((ch>>12) & 0x3F) | 0x80; - dest[2] = ((ch>>6) & 0x3F) | 0x80; - dest[3] = (ch & 0x3F) | 0x80; - return 4; - } - return 0; -} - -LtkTextManager * -ltk_init_text(char *font_name) -{ - LtkTextManager *tm = malloc(sizeof(LtkTextManager)); - if (!tm) { - (void)printf("Memory exhausted when trying to create text manager."); - exit(1); - } - tm->font_paths = kh_init(fontid); - tm->font_cache = kh_init(fontstruct); - tm->glyph_cache = kh_init(glyphcache); - tm->font_id_cur = 0; - ltk_load_default_font(tm, font_name); - - return tm; -} - -void -ltk_destroy_text_manager(LtkTextManager *tm) -{ - int k; - - kh_destroy(fontid, tm->font_paths); - - for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) { - if (kh_exist(tm->font_cache, k)) { - ltk_destroy_font(kh_value(tm->font_cache, k)); - } - } - kh_destroy(fontstruct, tm->font_cache); - - for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) { - if (kh_exist(tm->glyph_cache, k)) { - ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k)); - } - } - kh_destroy(glyphcache, tm->glyph_cache); - - free(tm); -} - -LtkGlyphInfo * -ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale) -{ - LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo)); - if (!glyph) { - (void)printf("Out of memory!\n"); - exit(1); - } - - glyph->id = id; - glyph->refs = 0; - glyph->alphamap = stbtt_GetGlyphBitmap( - &font->info, scale, scale, id, &glyph->w, - &glyph->h, &glyph->xoff, &glyph->yoff - ); - - return glyph; -} - -void -ltk_destroy_glyph_info(LtkGlyphInfo *gi) -{ - free(gi->alphamap); - free(gi); -} - -LtkGlyphInfo * -ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache) -{ - int ret; - khint_t k; - LtkGlyphInfo *glyph; - k = kh_get(glyphinfo, cache, id); - if (k == kh_end(cache)) { - glyph = ltk_create_glyph_info(font, id, scale); - /* FIXME: error checking with ret */ - k = kh_put(glyphinfo, cache, id, &ret); - kh_value(cache, k) = glyph; - } else { - glyph = kh_value(cache, k); - } - - return glyph; -} - -khint_t -ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size) -{ - khash_t(glyphinfo) *cache = kh_init(glyphinfo); - int ret; - khint_t k; - /* I guess I can just ignore ret for now */ - k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret); - kh_value(tm->glyph_cache, k) = cache; - - return k; -} - -void -ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache) -{ - int k; - for (k = kh_begin(cache); k != kh_end(cache); k++) { - if (kh_exist(cache, k)) { - ltk_destroy_glyph_info(kh_value(cache, k)); - } - } - kh_destroy(glyphinfo, cache); -} - -void -ltk_load_default_font(LtkTextManager *tm, char *name) -{ - FcPattern *match; - FcResult result; - char *file; - int index; - uint16_t font; - - tm->fcpattern = FcNameParse(name); - FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype"); - FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern); - FcDefaultSubstitute(tm->fcpattern); - match = FcFontMatch(NULL, tm->fcpattern, &result); - - FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file); - /* FIXME: Why is index never used? This is the index within the font file, - so it might be important, although I'm not sure if stb_truetype even - supports it */ - FcPatternGetInteger (match, FC_INDEX, 0, &index); - - tm->default_font = ltk_get_font(tm, file); - - FcPatternDestroy (match); -} - -LtkFont * -ltk_create_font(char *path, uint16_t id) -{ - long len; - LtkFont *font = malloc(sizeof(LtkFont)); - if (!font) { - (void)fprintf(stderr, "Out of memory!\n"); - exit(1); - } - char *contents = ltk_read_file(path, &len); - if (!stbtt_InitFont(&font->info, contents, 0)) - { - (void)fprintf(stderr, "Failed to load font %s\n", path); - exit(1); - } - /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */ - hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL); - hb_face_t *face = hb_face_create(blob, 0); - /* FIXME: need to use destroy function in order for the original file data to be freed? */ - hb_blob_destroy(blob); - font->hb = hb_font_create(face); - hb_face_destroy(face); - hb_ot_font_set_funcs(font->hb); - font->id = id; - font->refs = 0; - - return font; -} - -void -ltk_destroy_font(LtkFont *font) -{ - free(font->info.data); - hb_font_destroy(font->hb); - free(font); -} - -uint16_t -ltk_load_font(LtkTextManager *tm, char *path) -{ - LtkFont *font = ltk_create_font(path, tm->font_id_cur++); - int ret; - khint_t k; - /* FIXME: does kh_destroy also free these copied strings properly? */ - char *key = strdup(path); - k = kh_put(fontid, tm->font_paths, key, &ret); - kh_value(tm->font_paths, k) = font->id; - k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret); - kh_value(tm->font_cache, k) = font; - k = kh_get(fontid, tm->font_paths, path); - - return font->id; -} - -uint16_t -ltk_get_font(LtkTextManager *tm, char *path) -{ - int ret; - khint_t k; - uint16_t id; - k = kh_get(fontid, tm->font_paths, path); - if (k == kh_end(tm->font_paths)) { - id = ltk_load_font(tm, path); - } else { - id = kh_value(tm->font_paths, k); - } - - return id; -} - /* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then, or just use harfbuzz (then fribidi doesn't need to do any shaping) */ LtkTextLine * @@ -586,18 +309,6 @@ ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int len, ui } void -ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache) -{ - int k; - if (--glyph->info->refs < 1) { - k = kh_get(glyphinfo, cache, glyph->info->id); - kh_del(glyphinfo, cache, k); - ltk_destroy_glyph_info(glyph->info); - } - free(glyph); -} - -void ltk_destroy_text_segment(LtkTextSegment *ts) { LtkGlyph *glyph, *next_glyph; diff --git a/text-hb.h b/text-hb.h @@ -28,42 +28,9 @@ Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "stb_truetype.h", "khash.h", <harfbuzz/hb.h>, <fribidi.h>, -<fontconfig/fontconfig.h> +<fontconfig/fontconfig.h>, "text-common.h" */ -typedef struct { - stbtt_fontinfo info; - hb_font_t *hb; - uint16_t id; - unsigned int refs; -} LtkFont; - -/* Contains general info on glyphs that doesn't change regardless of the context */ -typedef struct _LtkGlyphInfo { - unsigned int id; - unsigned char *alphamap; - unsigned int w; - unsigned int h; - unsigned int xoff; /* x offset from origin to top left corner of glyph */ - unsigned int yoff; /* y offset from origin to top left corner of glyph */ - unsigned int refs; - /* FIXME: does refs need to be long? It could cause problems if a - program tries to cache/"keep alive" a lot of pages of text. */ -} LtkGlyphInfo; - -/* Contains glyph info specific to one run of text */ -typedef struct _LtkGlyph { - LtkGlyphInfo *info; - int x_offset; /* additional x offset given by harfbuzz */ - int y_offset; /* additional y offset given by harfbuzz */ - int x_advance; - int y_advance; - int x_abs; - int y_abs; - uint32_t cluster; /* index of char in original text - from harfbuzz */ - struct _LtkGlyph *next; -} LtkGlyph; - /* Single segment of text with same font */ typedef struct LtkTextSegment { uint16_t font_id; @@ -94,50 +61,6 @@ typedef struct { LtkTextSegment *start_segment; } LtkTextLine; -/* Hash definitions */ -/* glyph id -> glyph info struct */ -KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*) -/* font path, size -> glyph cache hash */ -KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*) -/* font path -> font id */ -KHASH_MAP_INIT_STR(fontid, uint16_t) -/* font id -> font struct */ -KHASH_MAP_INIT_INT(fontstruct, LtkFont*) - -typedef struct LtkTextManager { - khash_t(fontid) *font_paths; - khash_t(fontstruct) *font_cache; - khash_t(glyphcache) *glyph_cache; - FcPattern *fcpattern; - uint16_t default_font; - uint16_t font_id_cur; -} LtkTextManager; - -LtkTextManager *ltk_init_text(char *font_name); - -void ltk_destroy_text_manager(LtkTextManager *tm); - -LtkGlyphInfo *ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale); - -void ltk_destroy_glyph_info(LtkGlyphInfo *gi); - -LtkGlyphInfo *ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache); - -khint_t ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size); - -void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache); - -void ltk_load_default_font(LtkTextManager *tm, char *name); - -LtkFont *ltk_create_font(char *path, uint16_t id); - -void ltk_destroy_font(LtkFont *font); - -/* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */ -uint16_t ltk_load_font(LtkTextManager *tm, char *path); - -uint16_t ltk_get_font(LtkTextManager *tm, char *path); - /* TODO: different sizes, colors, styles, etc. */ LtkTextLine *ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size); @@ -145,8 +68,6 @@ LtkTextLine *ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fonti -> in case I want to get rid of uint_16_t, etc. */ LtkTextSegment *ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int text_len, uint16_t fontid, uint16_t size, hb_script_t script); -void ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache); - void ltk_destroy_text_segment(LtkTextSegment *ts); XImage *ltk_render_text_line(LtkTextLine *tl, Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg); @@ -154,4 +75,4 @@ XImage *ltk_render_text_line(LtkTextLine *tl, Display *dpy, Window window, GC gc void ltk_render_text_segment(LtkTextSegment *ts, unsigned int start_x, unsigned int start_y, XImage *img, XColor fg); -#endif +#endif /* _TEXT_HB_H_ */