text-hb.uberubernew.c - ltkx - GUI toolkit for X11 (old)
HTML git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
HTML git clone https://lumidify.org/ltkx.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
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 }