st

git clone git://mattcarlson.org/repos/st.git
Log | Files | Refs

hb.c (3633B)


      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 #include <math.h>
      4 #include <X11/Xft/Xft.h>
      5 #include <hb.h>
      6 #include <hb-ft.h>
      7 
      8 #include "st.h"
      9 
     10 void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length);
     11 hb_font_t *hbfindfont(XftFont *match);
     12 
     13 typedef struct {
     14     XftFont *match;
     15     hb_font_t *font;
     16 } HbFontMatch;
     17 
     18 static int hbfontslen = 0;
     19 static HbFontMatch *hbfontcache = NULL;
     20 
     21 void
     22 hbunloadfonts()
     23 {
     24     for (int i = 0; i < hbfontslen; i++) {
     25         hb_font_destroy(hbfontcache[i].font);
     26         XftUnlockFace(hbfontcache[i].match);
     27     }
     28 
     29     if (hbfontcache != NULL) {
     30         free(hbfontcache);
     31         hbfontcache = NULL;
     32     }
     33     hbfontslen = 0;
     34 }
     35 
     36 hb_font_t *
     37 hbfindfont(XftFont *match)
     38 {
     39     for (int i = 0; i < hbfontslen; i++) {
     40         if (hbfontcache[i].match == match)
     41             return hbfontcache[i].font;
     42     }
     43 
     44     /* Font not found in cache, caching it now. */
     45     hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1));
     46     FT_Face face = XftLockFace(match);
     47     hb_font_t *font = hb_ft_font_create(face, NULL);
     48     if (font == NULL)
     49         die("Failed to load Harfbuzz font.");
     50 
     51     hbfontcache[hbfontslen].match = match;
     52     hbfontcache[hbfontslen].font = font;
     53     hbfontslen += 1;
     54 
     55     return font;
     56 }
     57 
     58 void
     59 hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y)
     60 {
     61     int start = 0, length = 1, gstart = 0;
     62     hb_codepoint_t *codepoints = calloc(len, sizeof(hb_codepoint_t));
     63 
     64     for (int idx = 1, specidx = 1; idx < len; idx++) {
     65         if (glyphs[idx].mode & ATTR_WDUMMY) {
     66             length += 1;
     67             continue;
     68         }
     69 
     70         if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) {
     71             hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
     72 
     73             /* Reset the sequence. */
     74             length = 1;
     75             start = specidx;
     76             gstart = idx;
     77         } else {
     78             length += 1;
     79         }
     80 
     81         specidx++;
     82     }
     83 
     84     /* EOL. */
     85     hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
     86 
     87     /* Apply the transformation to glyph specs. */
     88     for (int i = 0, specidx = 0; i < len; i++) {
     89         if (glyphs[i].mode & ATTR_WDUMMY)
     90             continue;
     91         if (glyphs[i].mode & ATTR_BOXDRAW) {
     92             specidx++;
     93             continue;
     94         }
     95 
     96         if (codepoints[i] != specs[specidx].glyph)
     97             ((Glyph *)glyphs)[i].mode |= ATTR_LIGA;
     98 
     99         specs[specidx++].glyph = codepoints[i];
    100     }
    101 
    102     free(codepoints);
    103 }
    104 
    105 void
    106 hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length)
    107 {
    108     hb_font_t *font = hbfindfont(xfont);
    109     if (font == NULL)
    110         return;
    111 
    112     Rune rune;
    113     ushort mode = USHRT_MAX;
    114     hb_buffer_t *buffer = hb_buffer_create();
    115     hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
    116 
    117     /* Fill buffer with codepoints. */
    118     for (int i = start; i < (start+length); i++) {
    119         rune = string[i].u;
    120         mode = string[i].mode;
    121         if (mode & ATTR_WDUMMY)
    122             rune = 0x0020;
    123         hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
    124     }
    125 
    126     /* Shape the segment. */
    127     hb_shape(font, buffer, NULL, 0);
    128 
    129     /* Get new glyph info. */
    130     hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL);
    131 
    132     /* Write new codepoints. */
    133     for (int i = 0; i < length; i++) {
    134         hb_codepoint_t gid = info[i].codepoint;
    135         codepoints[start+i] = gid;
    136     }
    137 
    138     /* Cleanup. */
    139     hb_buffer_destroy(buffer);
    140 }