mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-07 15:37:14 +02:00
Fix a display of unicode composing characters
Reconstruct and use 'gui_macvim_draw_string' for CoreText renderer
This commit is contained in:
+39
-87
@@ -43,6 +43,7 @@
|
||||
#define DRAW_ITALIC 0x10 /* draw italic text */
|
||||
#define DRAW_CURSOR 0x20
|
||||
#define DRAW_WIDE 0x80 /* draw wide text */
|
||||
#define DRAW_COMP 0x100 /* drawing composing char */
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
|
||||
#define kCTFontOrientationDefault kCTFontDefaultOrientation
|
||||
@@ -1181,7 +1182,7 @@ attributedStringForString(NSString *string, const CTFontRef font,
|
||||
// 2 - full ligatures including rare
|
||||
// 1 - basic ligatures
|
||||
// 0 - no ligatures
|
||||
[NSNumber numberWithInteger:(useLigatures ? 1 : 0)],
|
||||
[NSNumber numberWithBool:useLigatures],
|
||||
kCTLigatureAttributeName,
|
||||
nil
|
||||
];
|
||||
@@ -1192,7 +1193,7 @@ attributedStringForString(NSString *string, const CTFontRef font,
|
||||
|
||||
static UniCharCount
|
||||
fetchGlyphsAndAdvances(const CTLineRef line, CGGlyph *glyphs, CGSize *advances,
|
||||
UniCharCount length)
|
||||
CGPoint *positions, UniCharCount length)
|
||||
{
|
||||
NSArray *glyphRuns = (NSArray*)CTLineGetGlyphRuns(line);
|
||||
|
||||
@@ -1211,6 +1212,8 @@ fetchGlyphsAndAdvances(const CTLineRef line, CGGlyph *glyphs, CGSize *advances,
|
||||
CTRunGetGlyphs(run, range, &glyphs[offset]);
|
||||
if (advances != NULL)
|
||||
CTRunGetAdvances(run, range, &advances[offset]);
|
||||
if (positions != NULL)
|
||||
CTRunGetPositions(run, range, &positions[offset]);
|
||||
|
||||
offset += count;
|
||||
if (offset >= length)
|
||||
@@ -1237,78 +1240,28 @@ gatherGlyphs(CGGlyph glyphs[], UniCharCount count)
|
||||
}
|
||||
|
||||
static UniCharCount
|
||||
ligatureGlyphsForChars(const unichar *chars, CGGlyph *glyphs,
|
||||
CGPoint *positions, UniCharCount length, CTFontRef font)
|
||||
composeGlyphsForChars(const unichar *chars, CGGlyph *glyphs,
|
||||
CGPoint *positions, UniCharCount length, CTFontRef font,
|
||||
BOOL isComposing, BOOL useLigatures)
|
||||
{
|
||||
// CoreText has no simple wait of retrieving a ligature for a set of
|
||||
// UniChars. The way proposed on the CoreText ML is to convert the text to
|
||||
// an attributed string, create a CTLine from it and retrieve the Glyphs
|
||||
// from the CTRuns in it.
|
||||
CGGlyph refGlyphs[length];
|
||||
CGPoint refPositions[length];
|
||||
|
||||
memcpy(refGlyphs, glyphs, sizeof(CGGlyph) * length);
|
||||
memcpy(refPositions, positions, sizeof(CGSize) * length);
|
||||
|
||||
memset(glyphs, 0, sizeof(CGGlyph) * length);
|
||||
|
||||
NSString *plainText = [NSString stringWithCharacters:chars length:length];
|
||||
CFAttributedStringRef ligatureText = attributedStringForString(plainText,
|
||||
font, YES);
|
||||
CFAttributedStringRef composedText = attributedStringForString(plainText,
|
||||
font,
|
||||
useLigatures);
|
||||
|
||||
CTLineRef ligature = CTLineCreateWithAttributedString(ligatureText);
|
||||
CTLineRef line = CTLineCreateWithAttributedString(composedText);
|
||||
|
||||
CGSize ligatureRanges[length], regularRanges[length];
|
||||
// get the (composing)glyphs and advances for the new text
|
||||
UniCharCount offset = fetchGlyphsAndAdvances(line, glyphs, NULL,
|
||||
isComposing ? positions : NULL,
|
||||
length);
|
||||
|
||||
// get the (ligature)glyphs and advances for the new text
|
||||
UniCharCount offset = fetchGlyphsAndAdvances(ligature, glyphs,
|
||||
ligatureRanges, length);
|
||||
// fetch the advances for the base text
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontOrientationDefault, refGlyphs,
|
||||
regularRanges, length);
|
||||
CFRelease(composedText);
|
||||
CFRelease(line);
|
||||
|
||||
CFRelease(ligatureText);
|
||||
CFRelease(ligature);
|
||||
|
||||
// tricky part: compare both advance ranges and chomp positions which are
|
||||
// covered by a single ligature while keeping glyphs not in the ligature
|
||||
// font.
|
||||
#define fequal(a, b) (fabs((a) - (b)) < FLT_EPSILON)
|
||||
#define fless(a, b)((a) - (b) < FLT_EPSILON) && (fabs((a) - (b)) > FLT_EPSILON)
|
||||
|
||||
CFIndex skip = 0;
|
||||
CFIndex i;
|
||||
for (i = 0; i < offset && skip + i < length; ++i) {
|
||||
memcpy(&positions[i], &refPositions[skip + i], sizeof(CGSize));
|
||||
|
||||
if (fequal(ligatureRanges[i].width, regularRanges[skip + i].width)) {
|
||||
// [mostly] same width
|
||||
continue;
|
||||
} else if (fless(ligatureRanges[i].width,
|
||||
regularRanges[skip + i].width)) {
|
||||
// original is wider than our result - use the original glyph
|
||||
// FIXME: this is currently the only way to detect emoji (except
|
||||
// for 'glyph[i] == 5')
|
||||
glyphs[i] = refGlyphs[skip + i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// no, that's a ligature
|
||||
// count how many positions this glyph would take up in the base text
|
||||
CFIndex j = 0;
|
||||
float width = ceil(regularRanges[skip + i].width);
|
||||
|
||||
while ((int)width < (int)ligatureRanges[i].width
|
||||
&& skip + i + j < length) {
|
||||
width += ceil(regularRanges[++j + skip + i].width);
|
||||
}
|
||||
skip += j;
|
||||
}
|
||||
|
||||
#undef fless
|
||||
#undef fequal
|
||||
|
||||
// as ligatures combine characters it is required to adjust the
|
||||
// as ligatures composing characters it is required to adjust the
|
||||
// original length value
|
||||
return offset;
|
||||
}
|
||||
@@ -1316,18 +1269,12 @@ ligatureGlyphsForChars(const unichar *chars, CGGlyph *glyphs,
|
||||
static void
|
||||
recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
|
||||
UniCharCount length, CGContextRef context, CTFontRef fontRef,
|
||||
NSMutableArray *fontCache, BOOL useLigatures)
|
||||
NSMutableArray *fontCache, BOOL isComposing, BOOL useLigatures)
|
||||
{
|
||||
if (CTFontGetGlyphsForCharacters(fontRef, chars, glyphs, length)) {
|
||||
// All chars were mapped to glyphs, so draw all at once and return.
|
||||
if (useLigatures) {
|
||||
length = ligatureGlyphsForChars(chars, glyphs, positions, length,
|
||||
fontRef);
|
||||
} else {
|
||||
// only fixup surrogate pairs if we're not using ligatures
|
||||
length = gatherGlyphs(glyphs, length);
|
||||
}
|
||||
|
||||
length = composeGlyphsForChars(chars, glyphs, positions, length,
|
||||
fontRef, isComposing, useLigatures);
|
||||
CTFontDrawGlyphs(fontRef, glyphs, positions, length, context);
|
||||
return;
|
||||
}
|
||||
@@ -1388,7 +1335,7 @@ recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
|
||||
return;
|
||||
|
||||
recurseDraw(chars, glyphs, positions, attemptedCount, context,
|
||||
fallback, fontCache, useLigatures);
|
||||
fallback, fontCache, isComposing, useLigatures);
|
||||
|
||||
// If only a portion of the invalid range was rendered above,
|
||||
// the remaining range needs to be attempted by subsequent
|
||||
@@ -1422,8 +1369,10 @@ recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
|
||||
float x = col*cellSize.width + insetSize.width;
|
||||
float y = frame.size.height - insetSize.height - (1+row)*cellSize.height;
|
||||
float w = cellSize.width;
|
||||
BOOL wide = flags & DRAW_WIDE ? YES : NO;
|
||||
BOOL composing = flags & DRAW_COMP ? YES : NO;
|
||||
|
||||
if (flags & DRAW_WIDE) {
|
||||
if (wide) {
|
||||
// NOTE: It is assumed that either all characters in 'chars' are wide
|
||||
// or all are normal width.
|
||||
w *= 2;
|
||||
@@ -1489,7 +1438,7 @@ recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
|
||||
if (length > maxlen) {
|
||||
if (glyphs) free(glyphs);
|
||||
if (positions) free(positions);
|
||||
glyphs = (CGGlyph*)malloc(length*sizeof(CGGlyph));
|
||||
glyphs = (CGGlyph*)calloc(length, sizeof(CGGlyph));
|
||||
positions = (CGPoint*)calloc(length, sizeof(CGPoint));
|
||||
maxlen = length;
|
||||
}
|
||||
@@ -1500,15 +1449,17 @@ recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
|
||||
CGContextSetFontSize(context, [font pointSize]);
|
||||
|
||||
// Calculate position of each glyph relative to (x,y).
|
||||
NSUInteger i;
|
||||
float xrel = 0;
|
||||
for (i = 0; i < length; ++i) {
|
||||
positions[i].x = xrel;
|
||||
xrel += w;
|
||||
if (!composing) {
|
||||
float xrel = 0;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
positions[i].x = xrel;
|
||||
positions[i].y = .0;
|
||||
xrel += w;
|
||||
}
|
||||
}
|
||||
|
||||
CTFontRef fontRef = (CTFontRef)(flags & DRAW_WIDE ? [fontWide retain]
|
||||
: [font retain]);
|
||||
CTFontRef fontRef = (CTFontRef)(wide ? [fontWide retain]
|
||||
: [font retain]);
|
||||
unsigned traits = 0;
|
||||
if (flags & DRAW_ITALIC)
|
||||
traits |= kCTFontItalicTrait;
|
||||
@@ -1517,7 +1468,7 @@ recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
|
||||
|
||||
if (traits) {
|
||||
CTFontRef fr = CTFontCreateCopyWithSymbolicTraits(fontRef, 0.0, NULL,
|
||||
traits, traits);
|
||||
traits, traits);
|
||||
if (fr) {
|
||||
CFRelease(fontRef);
|
||||
fontRef = fr;
|
||||
@@ -1525,7 +1476,8 @@ recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
|
||||
}
|
||||
|
||||
CGContextSetTextPosition(context, x, y+fontDescent);
|
||||
recurseDraw(chars, glyphs, positions, length, context, fontRef, fontCache, ligatures);
|
||||
recurseDraw(chars, glyphs, positions, length, context, fontRef, fontCache,
|
||||
composing, ligatures);
|
||||
|
||||
CFRelease(fontRef);
|
||||
if (thinStrokes)
|
||||
|
||||
+42
-50
@@ -477,41 +477,16 @@ gui_mch_delete_lines(int row, int num_lines)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gui_mch_draw_string(int row, int col, char_u *s, int len, int cells, int flags)
|
||||
{
|
||||
#ifdef FEAT_MBYTE
|
||||
char_u *conv_str = NULL;
|
||||
if (output_conv.vc_type != CONV_NONE) {
|
||||
conv_str = string_convert(&output_conv, s, &len);
|
||||
if (conv_str)
|
||||
s = conv_str;
|
||||
}
|
||||
#endif
|
||||
|
||||
[[MMBackend sharedInstance] drawString:s
|
||||
length:len
|
||||
row:row
|
||||
column:col
|
||||
cells:cells
|
||||
flags:flags];
|
||||
#ifdef FEAT_MBYTE
|
||||
if (conv_str)
|
||||
vim_free(conv_str);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags)
|
||||
{
|
||||
int c, cn, cl, i;
|
||||
MMBackend *backend = [MMBackend sharedInstance];
|
||||
#ifdef FEAT_MBYTE
|
||||
int c, cw, cl, ccl;
|
||||
int start = 0;
|
||||
int endcol = col;
|
||||
int startcol = col;
|
||||
BOOL wide = NO;
|
||||
MMBackend *backend = [MMBackend sharedInstance];
|
||||
#ifdef FEAT_MBYTE
|
||||
char_u *conv_str = NULL;
|
||||
|
||||
if (output_conv.vc_type != CONV_NONE) {
|
||||
@@ -519,45 +494,62 @@ gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags)
|
||||
if (conv_str)
|
||||
s = conv_str;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Loop over each character and output text when it changes from normal to
|
||||
// wide and vice versa.
|
||||
for (i = 0; i < len; i += cl) {
|
||||
for (int i = 0; i < len; i += cl) {
|
||||
c = utf_ptr2char(s + i);
|
||||
cn = utf_char2cells(c);
|
||||
cw = utf_char2cells(c);
|
||||
cl = utf_ptr2len(s + i);
|
||||
if (0 == cl)
|
||||
ccl = utfc_ptr2len(s + i);
|
||||
if (cl == 0)
|
||||
len = i; // len must be wrong (shouldn't happen)
|
||||
|
||||
if (!utf_iscomposing(c)) {
|
||||
if ((cn > 1 && !wide) || (cn <= 1 && wide)) {
|
||||
// Changed from normal to wide or vice versa.
|
||||
[backend drawString:(s+start) length:i-start
|
||||
row:row column:startcol
|
||||
cells:endcol-startcol
|
||||
flags:(wide ? flags|DRAW_WIDE : flags)];
|
||||
if (i > start && (cl < ccl || (cw > 1 && !wide) || (cw <= 1 && wide))) {
|
||||
// Changed from normal to wide or vice versa.
|
||||
[backend drawString:(s+start) length:i-start
|
||||
row:row column:startcol
|
||||
cells:endcol-startcol
|
||||
flags:flags|(wide ? DRAW_WIDE : 0)];
|
||||
|
||||
start = i;
|
||||
startcol = endcol;
|
||||
}
|
||||
start = i;
|
||||
startcol = endcol;
|
||||
}
|
||||
|
||||
wide = cn > 1;
|
||||
endcol += cn;
|
||||
wide = cw > 1;
|
||||
endcol += cw;
|
||||
|
||||
if (cl < ccl) {
|
||||
// Changed from normal to wide or vice versa.
|
||||
[backend drawString:(s+start) length:ccl
|
||||
row:row column:startcol
|
||||
cells:endcol-startcol
|
||||
flags:flags|DRAW_COMP|(wide ? DRAW_WIDE : 0)];
|
||||
|
||||
start = i + ccl;
|
||||
startcol = endcol;
|
||||
cl = ccl;
|
||||
}
|
||||
}
|
||||
|
||||
// Output remaining characters.
|
||||
[backend drawString:(s+start) length:len-start
|
||||
row:row column:startcol cells:endcol-startcol
|
||||
flags:(wide ? flags|DRAW_WIDE : flags)];
|
||||
if (len > start) {
|
||||
// Output remaining characters.
|
||||
[backend drawString:(s+start) length:len-start
|
||||
row:row column:startcol
|
||||
cells:endcol-startcol
|
||||
flags:flags|(wide ? DRAW_WIDE : 0)];
|
||||
}
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
if (conv_str)
|
||||
vim_free(conv_str);
|
||||
#endif
|
||||
|
||||
return endcol - col;
|
||||
#else
|
||||
[backend drawString:s length:len
|
||||
row:row column:col
|
||||
cells:len flags:flags];
|
||||
return len;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2469,16 +2469,11 @@ gui_outstr_nowrap(
|
||||
#ifdef FEAT_GUI_GTK
|
||||
/* The value returned is the length in display cells */
|
||||
len = gui_gtk2_draw_string(gui.row, col, s, len, draw_flags);
|
||||
#elif defined(FEAT_GUI_MACVIM)
|
||||
/* The value returned is the length in display cells */
|
||||
len = gui_macvim_draw_string(gui.row, col, s, len, draw_flags);
|
||||
#else
|
||||
# ifdef FEAT_MBYTE
|
||||
# ifdef FEAT_GUI_MACVIM
|
||||
if (use_gui_macvim_draw_string)
|
||||
{
|
||||
/* The value returned is the length in display cells */
|
||||
len = gui_macvim_draw_string(gui.row, col, s, len, draw_flags);
|
||||
}
|
||||
else
|
||||
# endif
|
||||
if (enc_utf8)
|
||||
{
|
||||
int start; /* index of bytes to be drawn */
|
||||
@@ -2512,9 +2507,6 @@ gui_outstr_nowrap(
|
||||
cells += cn;
|
||||
if (!comping || sep_comp)
|
||||
{
|
||||
# ifdef FEAT_GUI_MACVIM
|
||||
curr_wide = (cn > 1);
|
||||
# else
|
||||
if (cn > 1
|
||||
# ifdef FEAT_XFONTSET
|
||||
&& fontset == NOFONTSET
|
||||
@@ -2523,7 +2515,6 @@ gui_outstr_nowrap(
|
||||
curr_wide = TRUE;
|
||||
else
|
||||
curr_wide = FALSE;
|
||||
# endif
|
||||
}
|
||||
cl = utf_ptr2len(s + i);
|
||||
if (cl == 0) /* hit end of string */
|
||||
@@ -2555,13 +2546,7 @@ gui_outstr_nowrap(
|
||||
if (prev_wide)
|
||||
gui_mch_set_font(wide_font);
|
||||
gui_mch_draw_string(gui.row, scol, s + start, thislen,
|
||||
# ifdef FEAT_GUI_MACVIM
|
||||
cells,
|
||||
draw_flags | (prev_wide ? DRAW_WIDE : 0)
|
||||
# else
|
||||
draw_flag
|
||||
# endif
|
||||
);
|
||||
draw_flags);
|
||||
if (prev_wide)
|
||||
gui_mch_set_font(font);
|
||||
start += thislen;
|
||||
@@ -2591,17 +2576,13 @@ gui_outstr_nowrap(
|
||||
/* Draw a composing char on top of the previous char. */
|
||||
if (comping && sep_comp)
|
||||
{
|
||||
# if !defined(FEAT_GUI_MACVIM) && \
|
||||
(defined(__APPLE_CC__) && TARGET_API_MAC_CARBON)
|
||||
# if defined(__APPLE_CC__) && TARGET_API_MAC_CARBON
|
||||
/* Carbon ATSUI autodraws composing char over previous char */
|
||||
gui_mch_draw_string(gui.row, scol, s + i, cl,
|
||||
draw_flags | DRAW_TRANSP);
|
||||
# else
|
||||
gui_mch_draw_string(gui.row, scol - cn, s + i, cl,
|
||||
# ifdef FEAT_GUI_MACVIM
|
||||
0,
|
||||
# endif
|
||||
draw_flags | DRAW_TRANSP | DRAW_COMP);
|
||||
draw_flags | DRAW_TRANSP);
|
||||
# endif
|
||||
start = i + cl;
|
||||
}
|
||||
@@ -2613,11 +2594,7 @@ gui_outstr_nowrap(
|
||||
else
|
||||
# endif
|
||||
{
|
||||
gui_mch_draw_string(gui.row, col, s, len,
|
||||
# ifdef FEAT_GUI_MACVIM
|
||||
len,
|
||||
# endif
|
||||
draw_flags);
|
||||
gui_mch_draw_string(gui.row, col, s, len, draw_flags);
|
||||
# ifdef FEAT_MBYTE
|
||||
if (enc_dbcs == DBCS_JPNU)
|
||||
{
|
||||
|
||||
@@ -30,8 +30,6 @@ gui_mch_clear_all(void);
|
||||
gui_mch_clear_block(int row1, int col1, int row2, int col2);
|
||||
void
|
||||
gui_mch_delete_lines(int row, int num_lines);
|
||||
void
|
||||
gui_mch_draw_string(int row, int col, char_u *s, int len, int cells, int flags);
|
||||
int
|
||||
gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags);
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user