Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 39 additions & 87 deletions src/MacVim/MMCoreTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1181,7 +1182,7 @@ - (void)batchDrawData:(NSData *)data
// 2 - full ligatures including rare
// 1 - basic ligatures
// 0 - no ligatures
[NSNumber numberWithInteger:(useLigatures ? 1 : 0)],
[NSNumber numberWithBool:useLigatures],
kCTLigatureAttributeName,
nil
];
Expand All @@ -1192,7 +1193,7 @@ - (void)batchDrawData:(NSData *)data

static UniCharCount
fetchGlyphsAndAdvances(const CTLineRef line, CGGlyph *glyphs, CGSize *advances,
UniCharCount length)
CGPoint *positions, UniCharCount length)
{
NSArray *glyphRuns = (NSArray*)CTLineGetGlyphRuns(line);

Expand All @@ -1211,6 +1212,8 @@ - (void)batchDrawData:(NSData *)data
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)
Expand All @@ -1237,97 +1240,41 @@ - (void)batchDrawData:(NSData *)data
}

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);

CTLineRef ligature = CTLineCreateWithAttributedString(ligatureText);

CGSize ligatureRanges[length], regularRanges[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(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;
}
CFAttributedStringRef composedText = attributedStringForString(plainText,
font,
useLigatures);

// 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);
CTLineRef line = CTLineCreateWithAttributedString(composedText);

while ((int)width < (int)ligatureRanges[i].width
&& skip + i + j < length) {
width += ceil(regularRanges[++j + skip + i].width);
}
skip += j;
}
// get the (composing)glyphs and advances for the new text
UniCharCount offset = fetchGlyphsAndAdvances(line, glyphs, NULL,
isComposing ? positions : NULL,
length);

#undef fless
#undef fequal
CFRelease(composedText);
CFRelease(line);

// 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;
}

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;
}
Expand Down Expand Up @@ -1388,7 +1335,7 @@ - (void)batchDrawData:(NSData *)data
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
Expand Down Expand Up @@ -1422,8 +1369,10 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
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;
Expand Down Expand Up @@ -1489,7 +1438,7 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
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;
}
Expand All @@ -1500,15 +1449,17 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
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;
Expand All @@ -1517,15 +1468,16 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length

if (traits) {
CTFontRef fr = CTFontCreateCopyWithSymbolicTraits(fontRef, 0.0, NULL,
traits, traits);
traits, traits);
if (fr) {
CFRelease(fontRef);
fontRef = fr;
}
}

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)
Expand Down
92 changes: 42 additions & 50 deletions src/MacVim/gui_macvim.m
Original file line number Diff line number Diff line change
Expand Up @@ -477,87 +477,79 @@
}


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) {
conv_str = string_convert(&output_conv, s, &len);
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 = cw > 1;
endcol += cw;

wide = cn > 1;
endcol += cn;
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
}


Expand Down
Loading