URI: 
       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 }