text-hb.uberubernew.c (14858B)
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 typedef struct { 37 stbtt_fontinfo info; 38 hb_font_t *hb; 39 uint16_t id; 40 unsigned int refs; 41 } LtkFont; 42 43 /* Contains general info on glyphs that doesn't change regardless of the context */ 44 typedef struct _LtkGlyphInfo { 45 unsigned char *alphamap; 46 unsigned int w; 47 unsigned int h; 48 unsigned int xoff; /* x offset from origin to top left corner of glyph */ 49 unsigned int yoff; /* y offset from origin to top left corner of glyph */ 50 unsigned int refs; 51 /* FIXME: does refs need to be long? It could cause problems if a 52 program tries to cache/"keep alive" a lot of pages of text. */ 53 } LtkGlyphInfo; 54 55 /* Contains glyph info specific to one run of text */ 56 typedef struct _LtkGlyph { 57 LtkGlyphInfo *info; 58 int x_offset; /* additional x offset given by harfbuzz */ 59 int y_offset; /* additional y offset given by harfbuzz */ 60 int x_advance; 61 int y_advance; 62 uint32_t cluster; /* index of char in original text - from harfbuzz */ 63 struct _LtkGlyph *next; 64 } LtkGlyph; 65 66 typedef struct { 67 unsigned int w; 68 unsigned int h; 69 int start_x; 70 int start_y; 71 char *str; 72 LtkGlyph *start_glyph; 73 } LtkTextSegment; 74 75 /* Hash definitions */ 76 /* glyph id -> glyph info struct */ 77 KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*) 78 /* font path, size -> glyph cache hash */ 79 KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*) 80 /* font path -> font id */ 81 KHASH_MAP_INIT_STR(fontid, uint16_t) 82 /* font id -> font struct */ 83 KHASH_MAP_INIT_INT(fontstruct, LtkFont*) 84 85 typedef struct LtkTextManager_ { 86 khash_t(fontid) *font_paths; 87 khash_t(fontstruct) *font_cache; 88 khash_t(glyphcache) *glyph_cache; 89 uint16_t font_id_cur; 90 } LtkTextManager; 91 92 LtkTextManager * 93 ltk_init_text(void) 94 { 95 LtkTextManager *tm = malloc(sizeof(LtkTextManager)); 96 if (!tm) { 97 printf("Memory exhausted when trying to create text manager."); 98 exit(1); 99 } 100 tm->font_paths = kh_init(fontid); 101 tm->font_cache = kh_init(fontstruct); 102 tm->glyph_cache = kh_init(glyphcache); 103 tm->font_id_cur = 0; 104 105 return tm; 106 } 107 108 LtkGlyphInfo * 109 ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale) 110 { 111 LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo)); 112 if (!glyph) { 113 printf("Out of memory!\n"); 114 exit(1); 115 } 116 117 glyph->alphamap = stbtt_GetGlyphBitmap( 118 &font->info, scale, scale, id, &glyph->w, 119 &glyph->h, &glyph->xoff, &glyph->yoff 120 ); 121 122 return glyph; 123 } 124 125 LtkGlyphInfo * 126 ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache) 127 { 128 int ret; 129 khint_t k; 130 LtkGlyphInfo *glyph; 131 k = kh_get(glyphinfo, cache, id); 132 if (k == kh_end(cache)) { 133 glyph = ltk_create_glyph_info(font, id, scale); 134 glyph->refs = 0; 135 /* FIXME: error checking with ret */ 136 k = kh_put(glyphinfo, cache, id, &ret); 137 kh_value(cache, k) = glyph; 138 } else { 139 glyph = kh_value(cache, k); 140 glyph->refs++; 141 } 142 143 return glyph; 144 } 145 146 147 khint_t 148 ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size) 149 { 150 khash_t(glyphinfo) *cache = kh_init(glyphinfo); 151 int ret; 152 khint_t k; 153 /* I guess I can just ignore ret for now */ 154 k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret); 155 kh_value(tm->glyph_cache, k) = cache; 156 157 return k; 158 } 159 160 char * 161 ltk_load_file(const char *path, unsigned long *len) 162 { 163 FILE *f; 164 char *contents; 165 f = fopen(path, "rb"); 166 fseek(f, 0, SEEK_END); 167 *len = ftell(f); 168 fseek(f, 0, SEEK_SET); 169 contents = malloc(*len + 1); 170 fread(contents, 1, *len, f); 171 contents[*len] = '\0'; 172 fclose(f); 173 return contents; 174 } 175 176 unsigned long 177 ltk_blend_pixel(Display *display, Colormap colormap, XColor fg, XColor bg, double a) 178 { 179 if (a >= 1.0) { 180 return fg.pixel; 181 } else if (a == 0.0) { 182 return bg.pixel; 183 } 184 185 XColor blended; 186 blended.red = (int)((fg.red - bg.red) * a + bg.red); 187 blended.green = (int)((fg.green - bg.green) * a + bg.green); 188 blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue); 189 XAllocColor(display, colormap, &blended); 190 191 return blended.pixel; 192 } 193 194 LtkFont * 195 ltk_create_font(char *path, uint16_t id) 196 { 197 long len; 198 LtkFont *font = malloc(sizeof(LtkFont)); 199 if (!font) { 200 fprintf(stderr, "Out of memory!\n"); 201 exit(1); 202 } 203 char *contents = ltk_load_file(path, &len); 204 if (!stbtt_InitFont(&font->info, contents, 0)) 205 { 206 fprintf(stderr, "Failed to load font %s\n", path); 207 exit(1); 208 } 209 /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */ 210 hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL); 211 hb_face_t *face = hb_face_create(blob, 0); 212 /* FIXME: need to use destroy function in order for the original file data to be freed? */ 213 hb_blob_destroy(blob); 214 font->hb = hb_font_create(face); 215 hb_face_destroy(face); 216 hb_ot_font_set_funcs(font->hb); 217 font->id = id; 218 return font; 219 } 220 221 /* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */ 222 uint16_t 223 ltk_load_font(LtkTextManager *tm, char *path) 224 { 225 LtkFont *font = ltk_create_font(path, tm->font_id_cur++); 226 int ret; 227 khint_t k; 228 k = kh_put(fontid, tm->font_paths, path, &ret); 229 kh_value(tm->font_paths, k) = font->id; 230 k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret); 231 kh_value(tm->font_cache, k) = font; 232 233 return font->id; 234 } 235 236 uint16_t 237 ltk_get_font(LtkTextManager *tm, char *path) 238 { 239 int ret; 240 khint_t k; 241 uint16_t id; 242 k = kh_get(fontid, tm->font_paths, path); 243 if (k == kh_end(tm->font_paths)) { 244 id = ltk_load_font(tm, path); 245 } else { 246 id = kh_value(tm->font_paths, k); 247 } 248 249 return id; 250 } 251 252 /* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large 253 -> in case I want to get rid of uint_16_t, etc. */ 254 LtkTextSegment * 255 ltk_create_text_segment(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size) 256 { 257 /* (x1*, y1*): top left corner (relative to origin and absolute) 258 (x2*, y2*): bottom right corner (relative to origin and absolute) */ 259 LtkFont *font; 260 khash_t(glyphinfo) *glyph_cache; 261 khint_t k; 262 263 k = kh_get(fontstruct, tm->font_cache, fontid); 264 font = kh_value(tm->font_cache, k); 265 /* FIXME: when should refs be increased? maybe only at the end in case 266 it has to return for some other reason (out of memory, etc.) */ 267 font->refs++; 268 269 uint32_t attr = fontid << 16 + size; 270 /* FIXME: turn this int ltk_get_glyph_cache */ 271 k = kh_get(glyphcache, tm->glyph_cache, attr); 272 if (k == kh_end(tm->glyph_cache)) { 273 k = ltk_create_glyph_cache(tm, fontid, size); 274 } 275 glyph_cache = kh_value(tm->glyph_cache, k); 276 277 LtkTextSegment *ts = malloc(sizeof(LtkTextSegment)); 278 if (!ts) { 279 fprintf(stderr, "Out of memory!\n"); 280 exit(1); 281 } 282 ts->str = text; 283 284 hb_buffer_t *buf; 285 hb_glyph_info_t *ginf, *gi; 286 hb_glyph_position_t *gpos, *gp; 287 unsigned int text_len = 0; 288 int text_bytes = strlen(text); 289 if (text_bytes < 1) { 290 printf("WARNING: ltk_render_text_segment: length of text is less than 1.\n"); 291 } 292 293 buf = hb_buffer_create(); 294 hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT); 295 hb_buffer_add_utf8(buf, text, text_bytes, 0, text_bytes); 296 hb_buffer_guess_segment_properties(buf); 297 hb_shape(font->hb, buf, NULL, 0); 298 ginf = hb_buffer_get_glyph_infos(buf, &text_len); 299 gpos = hb_buffer_get_glyph_positions(buf, &text_len); 300 float scale = stbtt_ScaleForMappingEmToPixels(&font->info, size); 301 LtkGlyph *last_glyph = NULL; 302 303 int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN; 304 int x_abs = 0, y_abs = 0, x1_abs, y1_abs, x2_abs, y2_abs; 305 for (int i = 0; i < text_len; i++) { 306 gi = &ginf[i]; 307 gp = &gpos[i]; 308 LtkGlyph *glyph = malloc(sizeof(LtkGlyph)); 309 glyph->info = ltk_get_glyph_info(font, gi->codepoint, scale, glyph_cache); 310 /* FIXME: round instead of just casting */ 311 glyph->x_offset = (int)(gp->x_offset * scale); 312 glyph->y_offset = (int)(gp->y_offset * scale); 313 glyph->x_advance = (int)(gp->x_advance * scale); 314 glyph->y_advance = (int)(gp->y_advance * scale); 315 glyph->next = NULL; 316 if (i == 0) { 317 ts->start_glyph = glyph; 318 } else { 319 last_glyph->next = glyph; 320 } 321 last_glyph = glyph; 322 323 /* Calculate position in order to determine full size of text segment */ 324 x1_abs = x_abs + glyph->info->xoff + glyph->x_offset; 325 y1_abs = y_abs + glyph->info->yoff - glyph->y_offset; 326 x2_abs = x1_abs + glyph->info->w; 327 y2_abs = y1_abs + glyph->info->h; 328 if (x1_abs < x_min) x_min = x1_abs; 329 if (y1_abs < y_min) y_min = y1_abs; 330 if (x2_abs > x_max) x_max = x2_abs; 331 if (y2_abs > y_max) y_max = y2_abs; 332 x_abs += glyph->x_advance; 333 y_abs -= glyph->y_advance; 334 } 335 /* FIXME: what was this supposed to do? 336 I think it was supposed to be the start drawing position, but why would this not be 0? 337 I'm guessing it had something to do with the fact that I need to calculate where the 338 actual top left corner of the glyph is since harfbuzz gives me the origin - maybe I 339 should just store that position directly in LtkGlyph? Is there any need to advance, etc. 340 later on after I've positioned the glyphs? Well, I guess I'll figure that out eventually... */ 341 ts->start_x = -x_min; 342 ts->start_y = -y_min; 343 ts->w = x_max - x_min; 344 ts->h = y_max - y_min; 345 return ts; 346 } 347 348 XImage * 349 ltk_render_text_segment( 350 LtkTextSegment *ts, 351 Display *dpy, 352 Window window, 353 GC gc, 354 Colormap colormap, 355 XColor fg, 356 XColor bg) 357 { 358 XWindowAttributes attrs; 359 XGetWindowAttributes(dpy, window, &attrs); 360 int depth = attrs.depth; 361 XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, ts->w, ts->h, 32, 0); 362 img->data = calloc(img->bytes_per_line, img->height); 363 XInitImage(img); 364 int b; 365 for (int i = 0; i < ts->h; i++) { 366 b = img->bytes_per_line * i; 367 for (int j = 0; j < ts->w; j++) { 368 img->data[b++] = bg.blue / 257; 369 img->data[b++] = bg.green / 257; 370 img->data[b++] = bg.red / 257; 371 b++; 372 } 373 } 374 375 LtkGlyph *glyph = ts->start_glyph; 376 int x_cur = ts->start_x; 377 int y_cur = ts->start_y; 378 int x, y; 379 double a; 380 unsigned int out_r, out_g, out_b; 381 do { 382 x = x_cur + glyph->info->xoff + glyph->x_offset; 383 y = y_cur + glyph->info->yoff - glyph->y_offset; 384 for (int i = 0; i < glyph->info->h; i++) { 385 for (int j = 0; j < glyph->info->w; j++) { 386 b = (y + i) * img->bytes_per_line + (x + j) * 4; 387 a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0; 388 img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257; 389 img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257; 390 img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257; 391 392 /* 393 out_r = (fg.red / 255) * a1 + (uint8_t)img->data[b + 2] * (255 - a1); 394 out_g = (fg.green / 255) * a1 + (uint8_t)img->data[b + 1] * (255 - a1); 395 out_b = (fg.blue / 255) * a1 + (uint8_t)img->data[b] * (255 - a1); 396 out_r = (out_r + 1 + (out_r >> 8)) >> 8; 397 out_g = (out_g + 1 + (out_g >> 8)) >> 8; 398 out_b = (out_b + 1 + (out_b >> 8)) >> 8; 399 */ 400 } 401 } 402 x_cur += glyph->x_advance; 403 y_cur -= glyph->y_advance; 404 } while (glyph = glyph->next); 405 406 return img; 407 } 408 409 int main(int argc, char *argv[]) 410 { 411 Display *display; 412 int screen; 413 Window window; 414 GC gc; 415 416 unsigned long black, white; 417 Colormap colormap; 418 display = XOpenDisplay((char *)0); 419 screen = DefaultScreen(display); 420 colormap = DefaultColormap(display, screen); 421 black = BlackPixel(display, screen); 422 white = WhitePixel(display, screen); 423 XSetWindowAttributes wattr; 424 wattr.background_pixel = white; 425 wattr.border_pixel = black; 426 wattr.colormap = colormap; 427 window = XCreateWindow(display, DefaultRootWindow(display), 0, 0, 1366, 512, 0, 24, CopyFromParent, CopyFromParent, CWBackPixel | CWBorderPixel, &wattr); 428 XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL); 429 XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask); 430 gc = XCreateGC(display, window, 0, 0); 431 XSetBackground(display, gc, black); 432 XSetForeground(display, gc, black); 433 XClearWindow(display, window); 434 XMapRaised(display, window); 435 XColor c1, c2; 436 XParseColor(display, colormap, "#FFFFFF", &c1); 437 XParseColor(display, colormap, "#FF0000", &c2); 438 XAllocColor(display, colormap, &c1); 439 XAllocColor(display, colormap, &c2); 440 441 LtkTextManager *tm = ltk_init_text(); 442 uint16_t font_id = ltk_get_font(tm, "NotoNastaliqUrdu-Regular.ttf"); 443 uint16_t font_size = 100; 444 LtkTextSegment *text = ltk_create_text_segment(tm, "ہمارے بارے میں", font_id, font_size); 445 //Pixmap pix = ltk_render_text_segment(text, display, window, gc, colormap, c1, c2); 446 XImage *img = ltk_render_text_segment(text, display, window, gc, colormap, c1, c2); 447 //XCopyArea(display, pix, window, gc, 0, 0, text->w, text->h, 0, 0); 448 XPutImage(display, window, gc, img, 0, 0, 0, 0, text->w, text->h); 449 450 XEvent event; 451 KeySym key; 452 char txt[255]; 453 454 while(1) 455 { 456 XNextEvent(display, &event); 457 if (event.type == KeyPress && XLookupString(&event.xkey, txt, 255, &key, 0) == 1) 458 { 459 //XCopyArea(display, pix, window, gc, 0, 0, text->w, text->h, 0, 0); 460 XPutImage(display, window, gc, img, 0, 0, 0, 0, text->w, text->h); 461 if (txt[0] == 'q') 462 { 463 XFreeGC(display, gc); 464 XFreeColormap(display, colormap); 465 XDestroyWindow(display, window); 466 XCloseDisplay(display); 467 exit(0); 468 } 469 } 470 } 471 472 return 0; 473 }