ltkx

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

text1.c (9574B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <stdint.h>
      4 #include <X11/Xlib.h>
      5 #include <X11/Xutil.h>
      6 #include "stb_truetype.h"
      7 #include <harfbuzz/hb.h>
      8 #include <harfbuzz/hb-ot.h>
      9 #include <limits.h>
     10 
     11 /* These unicode routines are taken from
     12  * https://github.com/JeffBezanson/cutef8 */
     13 
     14 /* is c the start of a utf8 sequence? */
     15 #define isutf(c) (((c)&0xC0)!=0x80)
     16 
     17 static const uint32_t offsetsFromUTF8[6] = {
     18     0x00000000UL, 0x00003080UL, 0x000E2080UL,
     19     0x03C82080UL, 0xFA082080UL, 0x82082080UL
     20 };
     21 
     22 /* next character without NUL character terminator */
     23 uint32_t u8_nextmemchar(const char *s, size_t *i)
     24 {
     25     uint32_t ch = 0;
     26     size_t sz = 0;
     27     do {
     28         ch <<= 6;
     29         ch += (unsigned char)s[(*i)++];
     30         sz++;
     31     } while (!isutf(s[*i]));
     32     ch -= offsetsFromUTF8[sz-1];
     33 
     34     return ch;
     35 }
     36 
     37 
     38 #define STB_TRUETYPE_IMPLEMENTATION
     39 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
     40 
     41 stbtt_fontinfo ltk_load_font(const char *path)
     42 {
     43 	FILE *f;
     44 	long len;
     45 	char *contents;
     46 	stbtt_fontinfo info;
     47 	f = fopen(path, "rb");
     48 	fseek(f, 0, SEEK_END);
     49 	len = ftell(f);
     50 	fseek(f, 0, SEEK_SET);
     51 	contents = malloc(len + 1);
     52 	fread(contents, 1, len, f);
     53 	contents[len] = '\0';
     54 	fclose(f);
     55 	if (!stbtt_InitFont(&info, contents, 0))
     56 	{
     57 		fprintf(stderr, "Failed to load font %s\n", path);
     58 		exit(1);
     59 	}
     60 	return info;
     61 }
     62 
     63 char *ltk_load_file(const char *path, unsigned long *len)
     64 {
     65 	FILE *f;
     66 	char *contents;
     67 	f = fopen(path, "rb");
     68 	fseek(f, 0, SEEK_END);
     69 	*len = ftell(f);
     70 	fseek(f, 0, SEEK_SET);
     71 	contents = malloc(*len + 1);
     72 	fread(contents, 1, *len, f);
     73 	contents[*len] = '\0';
     74 	fclose(f);
     75 	return contents;
     76 }
     77 
     78 int ltk_text_width(uint8_t *text, stbtt_fontinfo fontinfo, int height)
     79 {
     80 	float scale = stbtt_ScaleForMappingEmToPixels(&fontinfo, height);
     81 	size_t i = 0;
     82 	int length = strlen(text);
     83 	if (length < 1) return 0;
     84 	int temp_x;
     85 	int kern_advance;
     86 	int width = 0;
     87 	uint32_t char1;
     88 	uint32_t char2;
     89 	char1 = u8_nextmemchar(text, &i);
     90 	while(i <= length)
     91 	{
     92 		stbtt_GetCodepointHMetrics(&fontinfo, char1, &temp_x, 0);
     93 		width += temp_x * scale;
     94 
     95 		char2 = u8_nextmemchar(text, &i);
     96 		if (!char2) break;
     97 		kern_advance = stbtt_GetCodepointKernAdvance(&fontinfo, char1, char2);
     98 		width += kern_advance * scale;
     99 		char1 = char2;
    100 	}
    101 
    102 	return width;
    103 }
    104 
    105 unsigned long ltk_blend_pixel(Display *display, Colormap colormap, XColor fg, XColor bg, double a)
    106 {
    107         XColor blended;
    108         if (a == 1.0)
    109                 return fg.pixel;
    110         else if (a == 0.0)
    111                 return bg.pixel;
    112         blended.red = (int)((fg.red - bg.red) * a + bg.red);
    113         blended.green = (int)((fg.green - bg.green) * a + bg.green);
    114         blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue);
    115         XAllocColor(display, colormap, &blended);
    116 
    117         return blended.pixel;
    118 }
    119 
    120 Pixmap ltk_render_text(
    121 	Display *display,
    122 	Window window,
    123 	GC gc,
    124 	uint8_t *text,
    125 	stbtt_fontinfo fontinfo,
    126 	int height,
    127 	XColor fg,
    128 	XColor bg,
    129 	const char *font_,
    130 	Colormap colormap,
    131 	int *w_total,
    132 	int *h_total
    133 )
    134 {
    135 	hb_blob_t *blob;
    136 	hb_face_t *face;
    137 	size_t filelen = 0;
    138 	char *filedata = ltk_load_file(font_, &filelen);
    139 	blob = hb_blob_create(filedata, filelen, HB_MEMORY_MODE_READONLY, NULL, NULL);
    140 	face = hb_face_create(blob, 0);
    141 	hb_blob_destroy(blob);
    142 	hb_font_t *hbfont = hb_font_create(face);
    143 	hb_face_destroy(face);
    144 	hb_ot_font_set_funcs(hbfont);
    145 
    146 	hb_buffer_t *buf;
    147 	hb_glyph_info_t *ginf;
    148 	hb_glyph_position_t *gpos;
    149 	unsigned int len = 0;
    150 
    151 	buf = hb_buffer_create();
    152 	hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
    153 	hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text));
    154 	hb_buffer_guess_segment_properties(buf);
    155 	hb_shape(hbfont, buf, NULL, 0);
    156 
    157 	ginf = hb_buffer_get_glyph_infos(buf, &len);
    158 	gpos = hb_buffer_get_glyph_positions(buf, &len);
    159 
    160 	XWindowAttributes attrs;
    161 	XGetWindowAttributes(display, window, &attrs);
    162 	int depth = attrs.depth;
    163 
    164 	int width = ltk_text_width(text, fontinfo, height) + 200;
    165 	float scale = stbtt_ScaleForMappingEmToPixels(&fontinfo, height);
    166 
    167 	int ascent, descent, line_gap;
    168 	stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &line_gap);
    169 	ascent *= scale;
    170 	descent *= scale;
    171 
    172 	Pixmap rendered = XCreatePixmap(display, window, width, height + 200, depth);
    173 	int length = strlen(text);
    174 	if (length < 1)
    175 	{
    176 		printf("WARNING: ltk_render_text: length of text is less than 1.\n");
    177 		return XCreatePixmap(display, window, 0, 0, depth);
    178 	}
    179 	int ax, x = 0, y = 0, x1, y1, x2, y2, byte_offset, kern_advance;
    180 	double x_abs = 0, y_abs = 0;
    181 	unsigned char *b;
    182 	int w, h, xoff, yoff;
    183 	int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
    184 	int x_min1, x_max1, y_min1, y_max1;
    185 	for (int i = 0; i < len; i++) {
    186 		hb_glyph_info_t *gi = &ginf[i];
    187 		hb_glyph_position_t *gp = &gpos[i];
    188 		stbtt_GetGlyphBitmapBox(&fontinfo, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
    189 		x_min1 = (int)(x_abs + x1 + gp->x_offset * scale);
    190 		y_min1 = (int)(y_abs + y1 - gp->y_offset * scale);
    191 		x_max1 = x_min1 + (x2 - x1);
    192 		y_max1 = y_min1 + (y2 - y1);
    193 		if (x_min1 < x_min) x_min = x_min1;
    194 		if (y_min1 < y_min) y_min = y_min1;
    195 		if (x_max1 > x_max) x_max = x_max1;
    196 		if (y_max1 > y_max) y_max = y_max1;
    197 		x_abs += (gp->x_advance * scale);
    198 		y_abs -= (gp->y_advance * scale);
    199 	}
    200 	x_abs = -x_min;
    201 	y_abs = -y_min;
    202 	*w_total = x_max - x_min;
    203 	*h_total = y_max - y_min;
    204 	/* FIXME: calloc checks for integer overflow, right? */
    205 	/* FIXME: check if null returned */
    206 	unsigned char *bitmap = calloc(*w_total * *h_total, sizeof(char));
    207 	for (int i = 0; i < len; i++) {
    208 		hb_glyph_info_t *gi = &ginf[i];
    209 		hb_glyph_position_t *gp = &gpos[i];
    210 		stbtt_GetGlyphBitmapBox(&fontinfo, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
    211 		b = stbtt_GetGlyphBitmap(&fontinfo, scale, scale, gi->codepoint, &w, &h, &xoff, &yoff);
    212 
    213 		//x = (int)(x_abs + xoff + gp->x_offset * scale);
    214 		//y = (int)(y_abs + yoff - gp->y_offset * scale);
    215 		x = (int)(x_abs + x1 + gp->x_offset * scale);
    216 		y = (int)(y_abs + y1 - gp->y_offset * scale);
    217 		for (int i = 0; i < h; i++)
    218 		{
    219 			for (int j = 0; j < w; j++)
    220 			{
    221 				byte_offset = (y + i) * *w_total + x + j;
    222 				bitmap[byte_offset] = bitmap[byte_offset] + b[i * w + j];
    223 				/* This *should* always be false, right? */
    224 				if (bitmap[byte_offset] > 255) bitmap[byte_offset] = 255;
    225 			}
    226 		}
    227 		free(b);
    228 
    229 		x_abs += (gp->x_advance * scale);
    230 		y_abs -= (gp->y_advance * scale);
    231 	}
    232 
    233                 XSetForeground(display, gc, bg.pixel);
    234                 for (int i = 0; i < *h_total; i++)
    235                 {
    236                         for (int j = 0; j < *w_total; j++)
    237                         {
    238                                 /* Yay! Magic! */
    239                                 XSetForeground(display, gc, ltk_blend_pixel(display, colormap, fg, bg, (bitmap[i * *w_total + j] / 255.0)));
    240                                 XDrawPoint(display, rendered, gc, j, i);
    241                         }
    242                 }
    243                 XSetForeground(display, gc, bg.pixel);
    244 
    245 	/* TODO: separate this into a separate function so that one function only creates
    246 	 * the bitmap and other functions to turn that into a pixmap with solid color
    247 	 * background, image background, etc.
    248 	 */
    249 	return rendered;
    250 }
    251 
    252 int main(int argc, char *argv[])
    253 {
    254     Display *display;
    255     int screen;
    256     Window window;
    257     GC gc;
    258 
    259     unsigned long black, white;
    260     Colormap colormap;
    261     display = XOpenDisplay((char *)0);
    262     screen = DefaultScreen(display);
    263     colormap = DefaultColormap(display, screen);
    264     black = BlackPixel(display, screen);
    265     white = WhitePixel(display, screen);
    266     window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 1366, 512, 0, black, white);
    267     XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
    268     XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
    269     gc = XCreateGC(display, window, 0, 0);
    270     XSetBackground(display, gc, black);
    271     XSetForeground(display, gc, black);
    272     XClearWindow(display, window);
    273     XMapRaised(display, window);
    274     XColor c1, c2;
    275     XParseColor(display, colormap, "#FFFFFF", &c1);
    276     XParseColor(display, colormap, "#FF0000", &c2);
    277     XAllocColor(display, colormap, &c1);
    278     XAllocColor(display, colormap, &c2);
    279 
    280 //    stbtt_fontinfo fontinfo = ltk_load_font("GentiumPlus-R.ttf");
    281  //   stbtt_fontinfo fontinfo = ltk_load_font("NotoNastaliqUrdu-Regular.ttf");
    282     stbtt_fontinfo fontinfo = ltk_load_font("Awami_beta3.ttf");
    283     int width = ltk_text_width("ہمارے بارے میںsdfasdfdsfasf", fontinfo, 200);
    284     int w, h;
    285 //    Pixmap pix = ltk_render_text(display, window, gc, "ہمارے بارے میں", fontinfo, 32, c1, c2, "NotoNastaliqUrdu-Regular.ttf", colormap, &w, &h);
    286     Pixmap pix = ltk_render_text(display, window, gc, "ہمارے بارے میں", fontinfo, 32, c1, c2, "Awami_beta3.ttf", colormap, &w, &h);
    287 //    Pixmap pix = ltk_render_text(display, window, gc, "Bob", fontinfo, 100, c1, c2, "GentiumPlus-R.ttf", colormap);
    288     XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0);
    289 
    290     XEvent event;
    291     KeySym key;
    292     char text[255];
    293 
    294     while(1)
    295     {
    296         XNextEvent(display, &event);
    297         if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
    298         {
    299             XCopyArea(display, pix, window, gc, 0, 0, width, 300, 0, 0);
    300             if (text[0] == 'q')
    301             {
    302                 XFreeGC(display, gc);
    303                 XFreeColormap(display, colormap);
    304                 XDestroyWindow(display, window);
    305                 XCloseDisplay(display);
    306                 exit(0);
    307             }
    308         }
    309     }
    310     
    311     return 0;
    312 }