URI: 
       text-hb.new.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.new.c (11742B)
       ---
            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 /* glyph id -> glyph info struct */
           44 KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*)
           45 /* font path, size -> glyph cache hash
           46 KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo))
           47 /* font path -> font id */
           48 KHASH_MAP_INIT_STR(fontid, uint16_t)
           49 /* font id -> font struct */
           50 KHASH_MAP_INIT_INT(fontstruct, LtkFont*)
           51 
           52 typedef struct LtkTextManager_ {
           53         khash_t(fontid) *font_paths;
           54         khash_t(fontstruct) *font_cache;
           55         khash_t(glyphcache) *glyph_cache;
           56         uint16_t font_id_cur;
           57 } LtkTextManager;
           58 
           59 typedef struct {
           60         stbtt_fontinfo info;
           61         hb_font_t *hb;
           62         uint16_t id;
           63 } LtkFont;
           64 
           65 LtkTextManager *
           66 ltk_init_text(void)
           67 {
           68         LtkTextManager *m = malloc(sizeof LtkTextManager);
           69         if (!m) ltk_fatal("Memory exhausted when trying to create text manager.");
           70         m->font_paths = kh_init(fontid);
           71         m->font_cache = kh_init(fontstruct);
           72         m->glyph_cache = kh_init(glyphcache);
           73         m->font_id_cur = 0;
           74         return m;
           75 }
           76 
           77 /* Contains general info on glyphs that doesn't change regardless of the context */
           78 typedef struct _LtkGlyphInfo {
           79         unsigned char *alphamap;
           80         unsigned int w;
           81         unsigned int h;
           82         unsigned int xoff; /* x offset from origin to top left corner of glyph */
           83         unsigned int yoff; /* y offset from origin to top left corner of glyph */
           84         unsigned int refs;
           85         /* FIXME: does refs need to be long? It could cause problems if a
           86         program tries to cache/"keep alive" a lot of pages of text. */
           87 } LtkGlyphInfo;
           88 
           89 LtkGlyphInfo *
           90 ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
           91 {
           92         LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
           93         if (!glyph) {
           94                 printf("Out of memory!\n");
           95                 exit(1);
           96         }
           97         
           98         glyph->alphamap = stbtt_GetGlyphBitmap(
           99                 &font->info, scale, scale, id, &glyph->w,
          100                 &glyph->h, &glyph->xoff, &glyph->yoff
          101         );
          102         return glyph;
          103 }
          104 
          105 LtkGlyphInfo *
          106 ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
          107 {
          108         int ret;
          109         khint_t k;
          110         LtkGlyphInfo *glyph;
          111         k = kh_get(glyphinfo, cache, id);
          112         if (k == kh_end(cache)) {
          113                 glyph = ltk_create_glyph_info(font, id, scale);
          114                 glyph->refs = 0;
          115                 /* FIXME: error checking with ret */
          116                 k = kh_put(glyphinfo, cache, glyph, &ret);
          117                 kh_value(cache, k) = glyph;
          118         } else {
          119                 glyph = kh_value(cache, k);
          120                 glyph->refs++;
          121         }
          122 
          123         return glyph;
          124 }
          125 
          126 
          127 void
          128 ltk_create_glyph_cache(LtkTextManager *m, uint16_t font_id, uint16_t font_size)
          129 {
          130         khash_t(glyphinfo) *cache = kh_init(glyphinfo);
          131         int ret;
          132         khint_t k;
          133         /* I guess I can just ignore ret for now */
          134         k = kh_put(glyphcache, m->glyph_cache, font_id << 16 + font_size, &ret);
          135         kh_value(m->glyph_cache, k) = cache;
          136 }
          137 
          138 char *
          139 ltk_load_file(const char *path, unsigned long *len)
          140 {
          141         FILE *f;
          142         char *contents;
          143         f = fopen(path, "rb");
          144         fseek(f, 0, SEEK_END);
          145         *len = ftell(f);
          146         fseek(f, 0, SEEK_SET);
          147         contents = malloc(*len + 1);
          148         fread(contents, 1, *len, f);
          149         contents[*len] = '\0';
          150         fclose(f);
          151         return contents;
          152 }
          153 
          154 unsigned long
          155 ltk_blend_pixel(Display *display, Colormap colormap, XColor fg, XColor bg, double a)
          156 {
          157         if (a >= 1.0) {
          158                 return fg.pixel;
          159         } else if (a == 0.0) {
          160                 return bg.pixel;
          161         }
          162 
          163         XColor blended;
          164         blended.red = (int)((fg.red - bg.red) * a + bg.red);
          165         blended.green = (int)((fg.green - bg.green) * a + bg.green);
          166         blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue);
          167         XAllocColor(display, colormap, &blended);
          168 
          169         return blended.pixel;
          170 }
          171 
          172 /* Contains glyph info specific to one run of text */
          173 typedef struct _LtkGlyph {
          174         LtkGlyphInfo *glyph_info;
          175         unsigned int x_offset; /* additional x offset given by harfbuzz */
          176         unsigned int y_offset; /* additional y offset given by harfbuzz */
          177         uint32_t cluster; /* index of char in original text - from harfbuzz */
          178         struct _LtkGlyph *next;
          179 } LtkGlyph;
          180 
          181 typedef struct {
          182         unsigned int width;
          183         unsigned int height;
          184         char *str;
          185         LtkGlyph *start_glyph;
          186 } LtkTextSegment;
          187 
          188 LtkFont *
          189 ltk_create_font(char *path, unsigned int id)
          190 {
          191         long len;
          192         LtkFont *font = malloc(sizeof(LtkFont));
          193         if (!font) {
          194                 fprintf(stderr, "Out of memory!\n");
          195                 exit(1);
          196         }
          197         char *contents = ltk_load_file(path, &len);
          198         if (!stbtt_InitFont(&font->font_info, contents, 0))
          199         {
          200                 fprintf(stderr, "Failed to load font %s\n", path);
          201                 exit(1);
          202         }
          203         hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
          204         hb_face_t *face = hb_face_create(blob, 0);
          205         hb_blob_destroy(blob);
          206         font->hb = hb_font_create(face);
          207         hb_face_destroy(face);
          208         hb_ot_font_set_funcs(font->hb);
          209         font->id = id;
          210         return font;
          211 }
          212 
          213 unsigned char *
          214 ltk_render_text_bitmap(
          215         uint8_t *text,
          216         LtkFont *font,
          217         int size,
          218         int *width,
          219         int *height)
          220 {
          221         /* (x1*, y1*): top left corner (relative to origin and absolute)
          222            (x2*, y2*): bottom right corner (relative to origin and absolute) */
          223         int x1, x2, y1, y2, x1_abs, x2_abs, y1_abs, y2_abs, x_abs = 0, y_abs = 0;
          224         int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
          225         int char_w, char_h, x_off, y_off;
          226 
          227         int byte_offset;
          228         int alpha;
          229         /* FIXME: Change to uint8_t? */
          230         unsigned char *bitmap;
          231         unsigned char *char_bitmap;
          232         hb_buffer_t *buf;
          233         hb_glyph_info_t *ginf, *gi;
          234         hb_glyph_position_t *gpos, *gp;
          235         unsigned int text_len = 0;
          236         int text_bytes = strlen(text);
          237         if (text_bytes < 1) {
          238                 printf("WARNING: ltk_render_text: length of text is less than 1.\n");
          239                 return "";
          240         }
          241 
          242         buf = hb_buffer_create();
          243         hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
          244         hb_buffer_add_utf8(buf, text, text_bytes, 0, text_bytes);
          245         hb_buffer_guess_segment_properties(buf);
          246         hb_shape(font->font, buf, NULL, 0);
          247         ginf = hb_buffer_get_glyph_infos(buf, &text_len);
          248         gpos = hb_buffer_get_glyph_positions(buf, &text_len);
          249         float scale = stbtt_ScaleForMappingEmToPixels(&font->font_info, size);
          250 
          251         /* Calculate size of bitmap */
          252         for (int i = 0; i < text_len; i++) {
          253                 gi = &ginf[i];
          254                 gp = &gpos[i];
          255                 stbtt_GetGlyphBitmapBox(&font->font_info, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
          256                 x1_abs = (int)(x_abs + x1 + gp->x_offset * scale);
          257                 y1_abs = (int)(y_abs + y1 - gp->y_offset * scale);
          258                 x2_abs = x1_abs + (x2 - x1);
          259                 y2_abs = y1_abs + (y2 - y1);
          260                 if (x1_abs < x_min) x_min = x1_abs;
          261                 if (y1_abs < y_min) y_min = y1_abs;
          262                 if (x2_abs > x_max) x_max = x2_abs;
          263                 if (y2_abs > y_max) y_max = y2_abs;
          264                 x_abs += (gp->x_advance * scale);
          265                 y_abs -= (gp->y_advance * scale);
          266         }
          267         x_abs = -x_min;
          268         y_abs = -y_min;
          269         *width = x_max - x_min;
          270         *height = y_max - y_min;
          271         /* FIXME: calloc checks for integer overflow, right? */
          272         /* FIXME: check if null returned */
          273         bitmap = calloc(*width * *height, sizeof(char));
          274         if (!bitmap) {
          275                 fprintf(stderr, "Can't allocate memory for bitmap!\n");
          276                 exit(1);
          277         }
          278         for (int i = 0; i < text_len; i++) {
          279                 gi = &ginf[i];
          280                 gp = &gpos[i];
          281                 stbtt_GetGlyphBitmapBox(&font->font_info, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
          282                 char_bitmap = stbtt_GetGlyphBitmap(&font->font_info, scale, scale, gi->codepoint, &char_w, &char_h, &x_off, &y_off);
          283 
          284                 x1_abs = (int)(x_abs + x1 + gp->x_offset * scale);
          285                 y1_abs = (int)(y_abs + y1 - gp->y_offset * scale);
          286                 for (int k = 0; k < char_h; k++)
          287                 {
          288                         for (int j = 0; j < char_w; j++)
          289                         {
          290                                 byte_offset = (y1_abs + k) * *width + x1_abs + j;
          291                                 alpha = bitmap[byte_offset] + char_bitmap[k * char_w + j];
          292                                 /* Cap at 255 so char doesn't overflow */
          293                                 bitmap[byte_offset] = alpha > 255 ? 255 : alpha;
          294                         }
          295                 }
          296                 free(char_bitmap);
          297 
          298                 x_abs += gp->x_advance * scale;
          299                 y_abs -= gp->y_advance * scale;
          300         }
          301         return bitmap;
          302 }
          303 
          304 Pixmap
          305 ltk_render_text(
          306         Display *dpy,
          307         Window window,
          308         GC gc,
          309         XColor fg,
          310         XColor bg,
          311         Colormap colormap,
          312         unsigned char *bitmap,
          313         int width,
          314         int height)
          315 {
          316         XWindowAttributes attrs;
          317         XGetWindowAttributes(dpy, window, &attrs);
          318         int depth = attrs.depth;
          319         Pixmap pix = XCreatePixmap(dpy, window, width, height, depth);
          320         XSetForeground(dpy, gc, bg.pixel);
          321         for (int i = 0; i < height; i++) {
          322                 for (int j = 0; j < width; j++) {
          323                         XSetForeground(dpy, gc, ltk_blend_pixel(dpy, colormap, fg, bg, bitmap[i * width + j] / 255.0));
          324                         XDrawPoint(dpy, pix, gc, j, i);
          325                 }
          326         }
          327         return pix;
          328 }
          329 
          330 int main(int argc, char *argv[])
          331 {
          332     Display *display;
          333     int screen;
          334     Window window;
          335     GC gc;
          336 
          337     unsigned long black, white;
          338     Colormap colormap;
          339     display = XOpenDisplay((char *)0);
          340     screen = DefaultScreen(display);
          341     colormap = DefaultColormap(display, screen);
          342     black = BlackPixel(display, screen);
          343     white = WhitePixel(display, screen);
          344     window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 1366, 512, 0, black, white);
          345     XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
          346     XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
          347     gc = XCreateGC(display, window, 0, 0);
          348     XSetBackground(display, gc, black);
          349     XSetForeground(display, gc, black);
          350     XClearWindow(display, window);
          351     XMapRaised(display, window);
          352     XColor c1, c2;
          353     XParseColor(display, colormap, "#FFFFFF", &c1);
          354     XParseColor(display, colormap, "#FF0000", &c2);
          355     XAllocColor(display, colormap, &c1);
          356     XAllocColor(display, colormap, &c2);
          357 
          358     LtkFont *font = ltk_load_font("NotoNastaliqUrdu-Regular.ttf");
          359     int w, h;
          360     unsigned char *bitmap = ltk_render_text_bitmap("ہمارے بارے میں", font, 256, &w, &h);
          361     Pixmap pix = ltk_render_text(display, window, gc, c1, c2, colormap, bitmap, w, h);
          362     XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0);
          363 
          364     XEvent event;
          365     KeySym key;
          366     char text[255];
          367 
          368     while(1)
          369     {
          370         XNextEvent(display, &event);
          371         if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
          372         {
          373             XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0);
          374             if (text[0] == 'q')
          375             {
          376                 XFreeGC(display, gc);
          377                 XFreeColormap(display, colormap);
          378                 XDestroyWindow(display, window);
          379                 XCloseDisplay(display);
          380                 exit(0);
          381             }
          382         }
          383     }
          384     
          385     return 0;
          386 }