Metal: draw composition glyph strings (combining accents, ligatures)
COMPOSITE_GLYPH had no branch in mtl_draw_glyph_string, so combining sequences (e + COMBINING ACUTE, etc.), ligatures and shaped scripts drew nothing (blank cells). Port ns_draw_composite_glyph_string_foreground to the Metal atlas: char2b[] holds glyph IDs indexed by the composition / gstring index and s->font is the composition's font; static compositions draw each glyph at its table offset, automatic ones walk the LGSTRING applying XOFF/YOFF/WADJUST adjustments, with sequential runs advanced by the natural font advance. Verified: "e+acute a+grave n+tilde o+diaeresis" now renders identically to NS (previously empty).
This commit is contained in:
parent
4f705960f5
commit
41b463decc
1 changed files with 135 additions and 32 deletions
167
src/mtlterm.m
167
src/mtlterm.m
|
|
@ -2060,6 +2060,99 @@ mtl_underline_metrics (struct glyph_string *s, int *position, int *thickness)
|
|||
*thickness = th;
|
||||
}
|
||||
|
||||
/* Draw glyph IDs s->char2b[FROM..TO) sequentially from (X, Y baseline),
|
||||
advancing each glyph by its natural font advance (composition runs are laid
|
||||
out by font metrics, not by the Emacs column grid). */
|
||||
static void
|
||||
mtl_draw_cmp_run (struct glyph_string *s, MtlFrameData *fd, CTFontRef ctfont,
|
||||
unsigned long fg, int from, int to, int x, int y)
|
||||
{
|
||||
float pen = (float) x;
|
||||
for (int k = from; k < to; k++)
|
||||
{
|
||||
MtlGlyphCacheEntry *ge = mtl_cache_glyph_id (ctfont, (CGGlyph) s->char2b[k]);
|
||||
if (!ge) continue;
|
||||
if (ge->width > 0)
|
||||
[fd drawGlyph:ge at:CGPointMake (pen, (float) y) color:fg];
|
||||
pen += ge->advance_x;
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw a composition glyph string: combining accents, ligatures and shaped
|
||||
scripts (Arabic, Indic). Mirrors ns_draw_composite_glyph_string_foreground
|
||||
but renders through the Metal glyph atlas instead of font->driver->draw.
|
||||
char2b[] holds glyph IDs indexed by the composition/gstring index (see
|
||||
fill_composite_glyph_string / fill_gstring_glyph_string) and s->font is the
|
||||
composition's font (it can differ from the face font). */
|
||||
static void
|
||||
mtl_draw_composite_glyph_string (struct glyph_string *s, MtlFrameData *fd,
|
||||
unsigned long fg)
|
||||
{
|
||||
int x;
|
||||
if (s->face && s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
|
||||
x = s->x + max (s->face->box_vertical_line_width, 0);
|
||||
else
|
||||
x = s->x;
|
||||
|
||||
CTFontRef ctfont = s->font ? (CTFontRef) macfont_get_nsctfont (s->font) : NULL;
|
||||
if (!ctfont || s->font_not_found_p)
|
||||
{
|
||||
/* Placeholder outline when the composition's font is missing. */
|
||||
if (s->cmp_from == 0)
|
||||
{
|
||||
unsigned long cc = ns_color_to_pixel (FRAME_CURSOR_COLOR (s->f));
|
||||
[fd fillRect:NSMakeRect (s->x, s->y, s->width - 1, 1) color:cc];
|
||||
[fd fillRect:NSMakeRect (s->x, s->y + s->height - 2, s->width - 1, 1) color:cc];
|
||||
[fd fillRect:NSMakeRect (s->x, s->y, 1, s->height - 1) color:cc];
|
||||
[fd fillRect:NSMakeRect (s->x + s->width - 2, s->y, 1, s->height - 1) color:cc];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s->first_glyph->u.cmp.automatic)
|
||||
{
|
||||
/* Static composition: each glyph at an explicit offset from the table. */
|
||||
int y = s->ybase;
|
||||
int i, j;
|
||||
for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
|
||||
if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
|
||||
{
|
||||
int xx = x + s->cmp->offsets[j * 2];
|
||||
int yy = y - s->cmp->offsets[j * 2 + 1];
|
||||
mtl_draw_cmp_run (s, fd, ctfont, fg, j, j + 1, xx, yy);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Automatic composition (shaping): LGLYPHs, some with adjustments. */
|
||||
Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
|
||||
int y = s->ybase;
|
||||
int width = 0, i, j;
|
||||
|
||||
for (i = j = s->cmp_from; i < s->cmp_to; i++)
|
||||
{
|
||||
Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
|
||||
if (NILP (LGLYPH_ADJUSTMENT (glyph)))
|
||||
width += LGLYPH_WIDTH (glyph);
|
||||
else
|
||||
{
|
||||
if (j < i)
|
||||
{
|
||||
mtl_draw_cmp_run (s, fd, ctfont, fg, j, i, x, y);
|
||||
x += width;
|
||||
}
|
||||
mtl_draw_cmp_run (s, fd, ctfont, fg, i, i + 1,
|
||||
x + LGLYPH_XOFF (glyph), y + LGLYPH_YOFF (glyph));
|
||||
x += LGLYPH_WADJUST (glyph);
|
||||
width = 0;
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
if (j < i)
|
||||
mtl_draw_cmp_run (s, fd, ctfont, fg, j, i, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mtl_draw_glyph_string_impl (struct glyph_string *s)
|
||||
{
|
||||
|
|
@ -2124,42 +2217,52 @@ mtl_draw_glyph_string_impl (struct glyph_string *s)
|
|||
/* Skip stretch glyphs (background already filled) */
|
||||
if (s->first_glyph->type == STRETCH_GLYPH) return;
|
||||
|
||||
/* Get CoreText font */
|
||||
CTFontRef ctfont = mtl_ctfont_for_face (face);
|
||||
if (!ctfont) { mtl_dgs_nofont_count++; return; }
|
||||
|
||||
|
||||
/* Advance using Emacs's own integer glyph grid (first_glyph[i].pixel_width),
|
||||
NOT the CoreText float advance. Re-advancing by the font's fractional
|
||||
advance drifts away from the layout Emacs computed: glyphs land at
|
||||
fractional positions (linear sampling blurs them) and progressively
|
||||
overlap/clip across the line. Keeping integer pen positions also makes the
|
||||
1:1 blit pixel-crisp. */
|
||||
int pen_x = s->x;
|
||||
int baseline_y = s->ybase;
|
||||
|
||||
for (int i = 0; i < s->nchars; i++)
|
||||
if (s->first_glyph->type == COMPOSITE_GLYPH)
|
||||
{
|
||||
/* char2b contains GLYPH IDs for the macfont backend — NOT Unicode codepoints.
|
||||
Use mtl_cache_glyph_id which calls CoreText with the ID directly. */
|
||||
CGGlyph glyphId = s->char2b ? (CGGlyph)s->char2b[i] : 0;
|
||||
int adv = (i < s->nchars) ? s->first_glyph[i].pixel_width
|
||||
: FRAME_COLUMN_WIDTH (f);
|
||||
/* Combining accents, ligatures, shaped scripts. */
|
||||
mtl_draw_composite_glyph_string (s, fd, fg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get CoreText font */
|
||||
CTFontRef ctfont = mtl_ctfont_for_face (face);
|
||||
if (!ctfont) { mtl_dgs_nofont_count++; return; }
|
||||
|
||||
if (glyphId)
|
||||
/* Advance using Emacs's own integer glyph grid
|
||||
(first_glyph[i].pixel_width), NOT the CoreText float advance.
|
||||
Re-advancing by the font's fractional advance drifts away from the
|
||||
layout Emacs computed: glyphs land at fractional positions (linear
|
||||
sampling blurs them) and progressively overlap/clip across the line.
|
||||
Keeping integer pen positions also makes the 1:1 blit pixel-crisp. */
|
||||
int pen_x = s->x;
|
||||
int baseline_y = s->ybase;
|
||||
|
||||
for (int i = 0; i < s->nchars; i++)
|
||||
{
|
||||
MtlGlyphCacheEntry *ge = mtl_cache_glyph_id (ctfont, glyphId);
|
||||
if (ge && ge->width > 0)
|
||||
{
|
||||
mtl_dgs_drawn_count++;
|
||||
/* bearing_y: distance from glyph top-left to baseline.
|
||||
In our top-left coord system, glyph top = baseline_y - bearing_y. */
|
||||
[fd drawGlyph:ge at:CGPointMake ((float)pen_x, (float)baseline_y)
|
||||
color:fg];
|
||||
}
|
||||
}
|
||||
/* char2b contains GLYPH IDs for the macfont backend — NOT Unicode
|
||||
codepoints. Use mtl_cache_glyph_id which calls CoreText with the
|
||||
ID directly. */
|
||||
CGGlyph glyphId = s->char2b ? (CGGlyph)s->char2b[i] : 0;
|
||||
int adv = (i < s->nchars) ? s->first_glyph[i].pixel_width
|
||||
: FRAME_COLUMN_WIDTH (f);
|
||||
|
||||
pen_x += adv;
|
||||
if (glyphId)
|
||||
{
|
||||
MtlGlyphCacheEntry *ge = mtl_cache_glyph_id (ctfont, glyphId);
|
||||
if (ge && ge->width > 0)
|
||||
{
|
||||
mtl_dgs_drawn_count++;
|
||||
/* bearing_y: distance from glyph top-left to baseline. In
|
||||
our top-left coord system, glyph top = baseline_y -
|
||||
bearing_y. */
|
||||
[fd drawGlyph:ge at:CGPointMake ((float)pen_x,
|
||||
(float)baseline_y)
|
||||
color:fg];
|
||||
}
|
||||
}
|
||||
|
||||
pen_x += adv;
|
||||
}
|
||||
}
|
||||
|
||||
/* Underline. Wave FIRST: FACE_UNDERLINE_WAVE is above FACE_UNDERLINE_SINGLE
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue