From bd370228a4a2432baa5f0ec6a39df27460fc6cf2 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Mon, 19 Dec 2022 10:09:06 +0100 Subject: [PATCH] ligatures: upgrading patch --- hb.c | 88 +++++++----------------------- hb.h | 10 +++- x.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 192 insertions(+), 82 deletions(-) diff --git a/hb.c b/hb.c index 7bf3768..dbd65c7 100644 --- a/hb.c +++ b/hb.c @@ -8,10 +8,10 @@ #include #include "st.h" +#include "hb.h" #define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } -void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length); hb_font_t *hbfindfont(XftFont *match); typedef struct { @@ -66,72 +66,23 @@ hbfindfont(XftFont *match) return font; } -void -hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y) -{ - int start = 0, length = 1, gstart = 0; - hb_codepoint_t *codepoints = calloc((unsigned int)len, sizeof(hb_codepoint_t)); +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) { + Rune rune; + ushort mode = USHRT_MAX; + unsigned int glyph_count; + int i, end = start + length; - for (int idx = 1, specidx = 1; idx < len; idx++) { - if (glyphs[idx].mode & ATTR_WDUMMY) { - length += 1; - continue; - } - - if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) { - hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); - - /* Reset the sequence. */ - length = 1; - start = specidx; - gstart = idx; - } else { - length += 1; - } - - specidx++; - } - - /* EOL. */ - hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); - - /* Apply the transformation to glyph specs. */ - for (int i = 0, specidx = 0; i < len; i++) { - if (glyphs[i].mode & ATTR_WDUMMY) - continue; - - #if BOXDRAW_PATCH - if (glyphs[i].mode & ATTR_BOXDRAW) { - specidx++; - continue; - } - #endif - - if (codepoints[i] != specs[specidx].glyph) - ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; - - specs[specidx++].glyph = codepoints[i]; - } - - free(codepoints); -} - -void -hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length) -{ hb_font_t *font = hbfindfont(xfont); if (font == NULL) return; - Rune rune; - ushort mode = USHRT_MAX; hb_buffer_t *buffer = hb_buffer_create(); hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); /* Fill buffer with codepoints. */ - for (int i = start; i < (start+length); i++) { - rune = string[i].u; - mode = string[i].mode; + for (i = start; i < end; i++) { + rune = glyphs[i].u; + mode = glyphs[i].mode; if (mode & ATTR_WDUMMY) rune = 0x0020; hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); @@ -141,14 +92,17 @@ hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoin hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t)); /* Get new glyph info. */ - hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count); + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); - /* Write new codepoints. */ - for (int i = 0; i < length; i++) { - hb_codepoint_t gid = info[i].codepoint; - codepoints[start+i] = gid; - } - - /* Cleanup. */ - hb_buffer_destroy(buffer); + /** Fill the output. */ + data->buffer = buffer; + data->glyphs = info; + data->positions = pos; + data->count = glyph_count; +} + +void hbcleanup(HbTransformData *data) { + hb_buffer_destroy(data->buffer); + memset(data, 0, sizeof(HbTransformData)); } diff --git a/hb.h b/hb.h index 07888df..3b0ef44 100644 --- a/hb.h +++ b/hb.h @@ -2,5 +2,13 @@ #include #include +typedef struct { + hb_buffer_t *buffer; + hb_glyph_info_t *glyphs; + hb_glyph_position_t *positions; + unsigned int count; +} HbTransformData; + void hbunloadfonts(); -void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); +void hbcleanup(HbTransformData *); diff --git a/x.c b/x.c index dc1a229..c2f5a59 100644 --- a/x.c +++ b/x.c @@ -80,6 +80,9 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, #else static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); #endif // WIDE_GLYPHS_PATCH +#if LIGATURES_PATCH +static void xresetfontsettings(ushort mode, Font **font, int *frcflags); +#endif // LIGATURES_PATCH static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); @@ -1605,6 +1608,24 @@ xinit(int cols, int rows) #endif // BOXDRAW_PATCH } +#if LIGATURES_PATCH +void +xresetfontsettings(ushort mode, Font **font, int *frcflags) +{ + *font = &dc.font; + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + *font = &dc.ibfont; + *frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + *font = &dc.ifont; + *frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + *font = &dc.bfont; + *frcflags = FRC_BOLD; + } +} +#endif // LIGATURES_PATCH + int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) { @@ -1624,6 +1645,14 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x FcFontSet *fcsets[] = { NULL }; FcCharSet *fccharset; int i, f, numspecs = 0; + #if LIGATURES_PATCH + int length = 0, start = 0; + HbTransformData shaped = { 0 }; + + /* Initial values. */ + mode = prevmode = glyphs[0].mode; + xresetfontsettings(mode, &font, &frcflags); + #endif // LIGATURES_PATCH #if VERTCENTER_PATCH for (i = 0, xp = winx, yp = winy + font->ascent + win.cyo; i < len; ++i) @@ -1637,17 +1666,143 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x historyOverlay(x+i, y, &g); rune = g.u; mode = g.mode; + #elif LIGATURES_PATCH + mode = glyphs[i].mode; #else rune = glyphs[i].u; mode = glyphs[i].mode; - #endif // VIM_BROWSE_PATCH + #endif // VIM_BROWSE_PATCH | LIGATURES_PATCH /* Skip dummy wide-character spacing. */ #if LIGATURES_PATCH if (mode & ATTR_WDUMMY) - #else + continue; + + if ( + prevmode != mode + || ATTRCMP(glyphs[start], glyphs[i]) + || selected(x + i, y) != selected(x + start, y) + || i == (len - 1) + ) { + /* Handle 1-character wide segments and end of line */ + length = i - start; + if (i == start) { + length = 1; + } else if (i == (len - 1)) { + length = (i - start + 1); + } + + /* Shape the segment. */ + hbtransform(&shaped, font->match, glyphs, start, length); + for (int code_idx = 0; code_idx < shaped.count; code_idx++) { + rune = glyphs[start + code_idx].u; + runewidth = win.cw * ((glyphs[start + code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f); + + if (glyphs[start + code_idx].mode & ATTR_WDUMMY) + continue; + + #if BOXDRAW_PATCH + if (glyphs[start + code_idx].mode & ATTR_BOXDRAW) { + /* minor shoehorning: boxdraw uses only this ushort */ + specs[numspecs].font = font->match; + specs[numspecs].glyph = boxdrawindex(&glyphs[start + code_idx]); + specs[numspecs].x = xp; + specs[numspecs].y = yp; + xp += runewidth; + numspecs++; + } else + #endif // BOXDRAW_PATCH + if (shaped.glyphs[code_idx].codepoint != 0) { + /* If symbol is found, put it into the specs. */ + specs[numspecs].font = font->match; + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; + specs[numspecs].x = xp + (short)shaped.positions[code_idx].x_offset; + specs[numspecs].y = yp + (short)shaped.positions[code_idx].y_offset; + xp += runewidth; + numspecs++; + } else { + /* If it's not found, try to fetch it through the font cache. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + } + + /* Cleanup and get ready for next segment. */ + hbcleanup(&shaped); + start = i; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + xresetfontsettings(mode, &font, &frcflags); + yp = winy + font->ascent; + } + } + #else // !LIGATURES_PATCH if (mode == ATTR_WDUMMY) - #endif // LIGATURES_PATCH continue; /* Determine font for glyph if different from previous glyph. */ @@ -1711,8 +1866,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x /* Nothing was found. Use fontconfig to find matching font. */ if (f >= frclen) { if (!font->set) - font->set = FcFontSort(0, font->pattern, - 1, 0, &fcres); + font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); fcsets[0] = font->set; /* @@ -1726,8 +1880,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, rune); - FcPatternAddCharSet(fcpattern, FC_CHARSET, - fccharset); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, 1); #if !USE_XFTFONTMATCH_PATCH @@ -1743,8 +1896,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x frc = xrealloc(frc, frccap * sizeof(Fontcache)); } - frc[frclen].font = XftFontOpenPattern(xw.dpy, - fontpattern); + frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); if (!frc[frclen].font) die("XftFontOpenPattern failed seeking fallback font: %s\n", strerror(errno)); @@ -1766,13 +1918,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x specs[numspecs].y = (short)yp; xp += runewidth; numspecs++; + #endif // LIGATURES_PATCH } - #if LIGATURES_PATCH - /* Harfbuzz transformation for ligatures. */ - hbtransform(specs, glyphs, len, x, y); - #endif // LIGATURES_PATCH - return numspecs; }