ltkx

GUI toolkit for X11 (old)
git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
git clone https://lumidify.org/ltkx.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
Log | Files | Refs | README | LICENSE

text-hb.c (11344B)


      1 /*
      2  * This file is part of the Lumidify ToolKit (LTK)
      3  * Copyright (c) 2017, 2018 lumidify <nobody@lumidify.org>
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a copy
      6  * of this software and associated documentation files (the "Software"), to deal
      7  * in the Software without restriction, including without limitation the rights
      8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9  * copies of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice shall be included in all
     13  * copies or substantial portions of the Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21  * SOFTWARE.
     22  */
     23 
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <stdint.h>
     27 #include <limits.h>
     28 #include <X11/Xlib.h>
     29 #include <X11/Xutil.h>
     30 #include <harfbuzz/hb.h>
     31 #include <harfbuzz/hb-ot.h>
     32 #define STB_TRUETYPE_IMPLEMENTATION
     33 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
     34 #include "khash.h"
     35 
     36 /* TODO: possibly "glyph manager" - only render glyph once and keep info in hash?
     37    -> would be difficult because of different sizes - would need to keep track of all that.
     38    -> reference counter - delete glyph from cache if not used anymore - good when there are many ligatures */
     39 
     40 /* Font manager: hash for font path -> font id
     41                  hash for font id -> ltk font struct */
     42 
     43 /* FIXME: this needs to be uint32_t, NOT just int! */
     44 /* font path, size -> glyph cache hash */
     45 KHASH_MAP_INIT_INT(ihash, khash_t(iglyph))
     46 /* glyph id -> glyph info struct */
     47 KHASH_MAP_INIT_INT(iglyph, LtkGlyphInfo*)
     48 /* font path -> font struct */
     49 KHASH_MAP_INIT_STR(cfont, LtkFont*)
     50 
     51 typedef struct LtkTextManager_ {
     52 	khash_t(cfont) *font_cache;
     53 	khash_t(ihash) *glyph_cache;
     54 	unsigned int font_id_cur;
     55 } LtkTextManager;
     56 
     57 /* Make LtkFont union of LtkFontHB, LtkFontGR, and LtkFontLT to handle harfbuzz, graphite, and normal latin? */
     58 typedef struct {
     59 	stbtt_fontinfo info;
     60 	hb_font_t *hb;
     61 	unsigned int id;
     62 } LtkFont;
     63 
     64 void
     65 ltk_render_glyph(LtkFont *font, unsigned int id, float scale, khash_t(iglyph) *cache)
     66 {
     67 	int x1, y1, x_off, y_off;
     68 	int ret;
     69 	khiter t;
     70 
     71 	LtkGlyphInfo *info = malloc(sizeof(LtkGlyphInfo));
     72 	info->alphamap = stbtt_GetGlyphBitmap(&font->font_info, scale, scale, id, info->w, info->h, info->x_topleft, info->y_topleft);
     73 	k = kh_put(iglyph, cache, info, &ret);
     74 	/* x/y_advance? */
     75 }
     76 
     77 LtkTextManager *
     78 ltk_init_text(void)
     79 {
     80 	LtkTextManager *m = malloc(sizeof LtkTextManager);
     81 	if (!m) ltk_fatal("Memory exhausted when trying to create text manager.");
     82 	m->font_cache = kh_init(cfont);
     83 	m->glyph_cache = kh_init(ihash);
     84 	m->font_id_cur = 0;
     85 	return m;
     86 }
     87 
     88 void
     89 ltk_create_glyph_cache(LtkTextManager *m, unsigned int font_id, unsigned int font_size)
     90 {
     91 	khash_t(iglyph) *cache = kh_init(iglyph);
     92 	int ret;
     93 	khiter_t k;
     94 	/* I guess I can just ignore ret for now */
     95 	k = kh_put(iglyph, m->glyph_cache, cache, &ret);
     96 }
     97 
     98 
     99 
    100 char *
    101 ltk_load_file(const char *path, unsigned long *len)
    102 {
    103 	FILE *f;
    104 	char *contents;
    105 	f = fopen(path, "rb");
    106 	fseek(f, 0, SEEK_END);
    107 	*len = ftell(f);
    108 	fseek(f, 0, SEEK_SET);
    109 	contents = malloc(*len + 1);
    110 	fread(contents, 1, *len, f);
    111 	contents[*len] = '\0';
    112 	fclose(f);
    113 	return contents;
    114 }
    115 
    116 unsigned long
    117 ltk_blend_pixel(Display *display, Colormap colormap, XColor fg, XColor bg, double a)
    118 {
    119         if (a >= 1.0) {
    120 		return fg.pixel;
    121         } else if (a == 0.0) {
    122 		return bg.pixel;
    123 	}
    124 
    125         XColor blended;
    126         blended.red = (int)((fg.red - bg.red) * a + bg.red);
    127         blended.green = (int)((fg.green - bg.green) * a + bg.green);
    128         blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue);
    129         XAllocColor(display, colormap, &blended);
    130 
    131         return blended.pixel;
    132 }
    133 
    134 /* Contains general info on glyphs that doesn't change regardless of the context */
    135 typedef struct _LtkGlyphInfo {
    136 	unsigned char *alphamap;
    137 	unsigned int w;
    138 	unsigned int h;
    139 	unsigned int x_topleft; /* x offset from origin to top left corner of glyph */
    140 	unsigned int y_topleft; /* y offset from origin to top left corner of glyph */
    141 } LtkGlyphInfo;
    142 
    143 /* Contains glyph info specific to one run of text */
    144 typedef struct _LtkGlyph {
    145 	LtkGlyphInfo *glyph_info;
    146 	/* Does all this need to be stored or are just the top left coordinates needed? */
    147 	unsigned int x; /* top left x coordinate */
    148 	unsigned int y; /* top left y coordinate */
    149 	unsigned int x_offset; /* additional x offset given by harfbuzz */
    150 	unsigned int y_offset; /* additional y offset given by harfbuzz */
    151 	unsigned int x_advance;
    152 	unsigned int y_advance;
    153 	uint32_t cluster; /* index of char in original text - from harfbuzz */
    154 	struct _LtkGlyph *next;
    155 } LtkGlyph;
    156 
    157 typedef struct {
    158 	unsigned int width;
    159 	unsigned int height;
    160 	char *str;
    161 	LtkGlyph *start_glyph;
    162 } LtkTextSegment;
    163 
    164 LtkFont *
    165 ltk_load_font(char *path, unsigned int id)
    166 {
    167 	long len;
    168 	LtkFont *font = malloc(sizeof(LtkFont));
    169 	if (!font) {
    170 		fprintf(stderr, "Out of memory!\n");
    171 		exit(1);
    172 	}
    173 	char *contents = ltk_load_file(path, &len);
    174 	if (!stbtt_InitFont(&font->font_info, contents, 0))
    175 	{
    176 		fprintf(stderr, "Failed to load font %s\n", path);
    177 		exit(1);
    178 	}
    179 	hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
    180 	hb_face_t *face = hb_face_create(blob, 0);
    181 	hb_blob_destroy(blob);
    182 	font->hb = hb_font_create(face);
    183 	hb_face_destroy(face);
    184 	hb_ot_font_set_funcs(font->hb);
    185 	font->id = id;
    186 	return font;
    187 }
    188 
    189 unsigned char *
    190 ltk_render_text_bitmap(
    191 	uint8_t *text,
    192 	LtkFont *font,
    193 	int size,
    194 	int *width,
    195 	int *height)
    196 {
    197 	/* (x1*, y1*): top left corner (relative to origin and absolute)
    198 	   (x2*, y2*): bottom right corner (relative to origin and absolute) */
    199 	int x1, x2, y1, y2, x1_abs, x2_abs, y1_abs, y2_abs, x_abs = 0, y_abs = 0;
    200 	int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
    201 	int char_w, char_h, x_off, y_off;
    202 
    203 	int byte_offset;
    204 	int alpha;
    205 	/* FIXME: Change to uint8_t? */
    206 	unsigned char *bitmap;
    207 	unsigned char *char_bitmap;
    208 	hb_buffer_t *buf;
    209 	hb_glyph_info_t *ginf, *gi;
    210 	hb_glyph_position_t *gpos, *gp;
    211 	unsigned int text_len = 0;
    212 	int text_bytes = strlen(text);
    213 	if (text_bytes < 1) {
    214 		printf("WARNING: ltk_render_text: length of text is less than 1.\n");
    215 		return "";
    216 	}
    217 
    218 	buf = hb_buffer_create();
    219 	hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
    220 	hb_buffer_add_utf8(buf, text, text_bytes, 0, text_bytes);
    221 	hb_buffer_guess_segment_properties(buf);
    222 	hb_shape(font->font, buf, NULL, 0);
    223 	ginf = hb_buffer_get_glyph_infos(buf, &text_len);
    224 	gpos = hb_buffer_get_glyph_positions(buf, &text_len);
    225 	float scale = stbtt_ScaleForMappingEmToPixels(&font->font_info, size);
    226 
    227 	/* Calculate size of bitmap */
    228 	for (int i = 0; i < text_len; i++) {
    229 		gi = &ginf[i];
    230 		gp = &gpos[i];
    231 		stbtt_GetGlyphBitmapBox(&font->font_info, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
    232 		x1_abs = (int)(x_abs + x1 + gp->x_offset * scale);
    233 		y1_abs = (int)(y_abs + y1 - gp->y_offset * scale);
    234 		x2_abs = x1_abs + (x2 - x1);
    235 		y2_abs = y1_abs + (y2 - y1);
    236 		if (x1_abs < x_min) x_min = x1_abs;
    237 		if (y1_abs < y_min) y_min = y1_abs;
    238 		if (x2_abs > x_max) x_max = x2_abs;
    239 		if (y2_abs > y_max) y_max = y2_abs;
    240 		x_abs += (gp->x_advance * scale);
    241 		y_abs -= (gp->y_advance * scale);
    242 	}
    243 	x_abs = -x_min;
    244 	y_abs = -y_min;
    245 	*width = x_max - x_min;
    246 	*height = y_max - y_min;
    247 	/* FIXME: calloc checks for integer overflow, right? */
    248 	/* FIXME: check if null returned */
    249 	bitmap = calloc(*width * *height, sizeof(char));
    250 	if (!bitmap) {
    251 		fprintf(stderr, "Can't allocate memory for bitmap!\n");
    252 		exit(1);
    253 	}
    254 	for (int i = 0; i < text_len; i++) {
    255 		gi = &ginf[i];
    256 		gp = &gpos[i];
    257 		stbtt_GetGlyphBitmapBox(&font->font_info, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
    258 		char_bitmap = stbtt_GetGlyphBitmap(&font->font_info, scale, scale, gi->codepoint, &char_w, &char_h, &x_off, &y_off);
    259 
    260 		x1_abs = (int)(x_abs + x1 + gp->x_offset * scale);
    261 		y1_abs = (int)(y_abs + y1 - gp->y_offset * scale);
    262 		for (int k = 0; k < char_h; k++)
    263 		{
    264 			for (int j = 0; j < char_w; j++)
    265 			{
    266 				byte_offset = (y1_abs + k) * *width + x1_abs + j;
    267 				alpha = bitmap[byte_offset] + char_bitmap[k * char_w + j];
    268 				/* Cap at 255 so char doesn't overflow */
    269 				bitmap[byte_offset] = alpha > 255 ? 255 : alpha;
    270 			}
    271 		}
    272 		free(char_bitmap);
    273 
    274 		x_abs += gp->x_advance * scale;
    275 		y_abs -= gp->y_advance * scale;
    276 	}
    277 	return bitmap;
    278 }
    279 
    280 Pixmap
    281 ltk_render_text(
    282 	Display *dpy,
    283 	Window window,
    284 	GC gc,
    285 	XColor fg,
    286 	XColor bg,
    287 	Colormap colormap,
    288 	unsigned char *bitmap,
    289 	int width,
    290 	int height)
    291 {
    292 	XWindowAttributes attrs;
    293 	XGetWindowAttributes(dpy, window, &attrs);
    294 	int depth = attrs.depth;
    295 	Pixmap pix = XCreatePixmap(dpy, window, width, height, depth);
    296 	XSetForeground(dpy, gc, bg.pixel);
    297 	for (int i = 0; i < height; i++) {
    298 		for (int j = 0; j < width; j++) {
    299 			XSetForeground(dpy, gc, ltk_blend_pixel(dpy, colormap, fg, bg, bitmap[i * width + j] / 255.0));
    300 			XDrawPoint(dpy, pix, gc, j, i);
    301 		}
    302 	}
    303 	return pix;
    304 }
    305 
    306 int main(int argc, char *argv[])
    307 {
    308     Display *display;
    309     int screen;
    310     Window window;
    311     GC gc;
    312 
    313     unsigned long black, white;
    314     Colormap colormap;
    315     display = XOpenDisplay((char *)0);
    316     screen = DefaultScreen(display);
    317     colormap = DefaultColormap(display, screen);
    318     black = BlackPixel(display, screen);
    319     white = WhitePixel(display, screen);
    320     window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 1366, 512, 0, black, white);
    321     XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
    322     XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
    323     gc = XCreateGC(display, window, 0, 0);
    324     XSetBackground(display, gc, black);
    325     XSetForeground(display, gc, black);
    326     XClearWindow(display, window);
    327     XMapRaised(display, window);
    328     XColor c1, c2;
    329     XParseColor(display, colormap, "#FFFFFF", &c1);
    330     XParseColor(display, colormap, "#FF0000", &c2);
    331     XAllocColor(display, colormap, &c1);
    332     XAllocColor(display, colormap, &c2);
    333 
    334     LtkFont *font = ltk_load_font("NotoNastaliqUrdu-Regular.ttf");
    335     int w, h;
    336     unsigned char *bitmap = ltk_render_text_bitmap("ہمارے بارے میں", font, 256, &w, &h);
    337     Pixmap pix = ltk_render_text(display, window, gc, c1, c2, colormap, bitmap, w, h);
    338     XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0);
    339 
    340     XEvent event;
    341     KeySym key;
    342     char text[255];
    343 
    344     while(1)
    345     {
    346         XNextEvent(display, &event);
    347         if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
    348         {
    349             XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0);
    350             if (text[0] == 'q')
    351             {
    352                 XFreeGC(display, gc);
    353                 XFreeColormap(display, colormap);
    354                 XDestroyWindow(display, window);
    355                 XCloseDisplay(display);
    356                 exit(0);
    357             }
    358         }
    359     }
    360     
    361     return 0;
    362 }