URI: 
       text-hb.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.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 }