diff --git a/src/MacVim/MMCoreTextView.m b/src/MacVim/MMCoreTextView.m index 9ee0f654e1..e53eb6f9ea 100644 --- a/src/MacVim/MMCoreTextView.m +++ b/src/MacVim/MMCoreTextView.m @@ -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 @@ - (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 ]; @@ -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); @@ -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) @@ -1237,78 +1240,28 @@ - (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; } @@ -1316,18 +1269,12 @@ - (void)batchDrawData:(NSData *)data 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 @@ - (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 @@ -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; @@ -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; } @@ -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; @@ -1517,7 +1468,7 @@ - (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; @@ -1525,7 +1476,8 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length } 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) diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index 4719f1cc76..d6fa0a0929 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -477,41 +477,16 @@ } - 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 @@ 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 } diff --git a/src/gui.c b/src/gui.c index 6fd6db514c..66067965e5 100644 --- a/src/gui.c +++ b/src/gui.c @@ -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) { diff --git a/src/proto/gui_macvim.pro b/src/proto/gui_macvim.pro index cdf3e0058d..dd75a6f2b3 100644 --- a/src/proto/gui_macvim.pro +++ b/src/proto/gui_macvim.pro @@ -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